【BZOJ1040】[ZJOI2008]骑士

题目链接

算法:

          对于每一个骑士,我们由他痛恨的骑士向他自己连一条边,由此整个图组成了若干个联通块,而每个联通块正式一个基环树,则答案就是两条边只选一个点的权值之和的最大值,那么我们设f[i][0\1]表示i这个节点选\不选的最优战斗力,那么对于每一个联通块,我们可以找出环上的某一条边,暂时把它隔开,分两种情况,一种选左边那个骑士,另一种选右边那个骑士,分别在树上DP,然后取最大值即可。

Code:

#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
template<typename T> void read(T &num){
    char c=getchar();num=0;T f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){num=(num<<3)+(num<<1)+(c^48);c=getchar();}
    num*=f;
}
template<typename T> void qwq(T x){
    if(x>9)qwq(x/10);
    putchar(x%10+'0');
}
template<typename T> void write(T x){
    if(x<0){x=-x;putchar('-');}
    qwq(x);putchar('\n');
}
template<typename T> void chkmax(T &x,T y){x=x>y?x:y;}
struct wzy{
    int nxt,vertice;
}edge[1000010];
int head[1000010];int len=0;
inline void add_edge(int x,int y){
    edge[++len].nxt=head[x];edge[len].vertice=y;head[x]=len;return;
}

long long ans=0;
int co[1000010][2];long long f[1000010][2];
bool vis[1000010];
inline void dfs(int x,int rp){
    f[x][0]=0;f[x][1]=co[x][0];vis[x]=1;
    for(int i=head[x];i;i=edge[i].nxt){
        int nop=edge[i].vertice;
        if(nop==rp){f[nop][1]=-(1e14);continue;}
        dfs(nop,rp);
        f[x][0]+=max(f[nop][0],f[nop][1]);f[x][1]+=f[nop][0];
    }	
    return;
}
inline void dp(int x){
    int pos=x;vis[x]=1;
    while(!vis[co[pos][1]]){pos=co[pos][1];vis[pos]=1;}
    long long ret=0; 
    dfs(pos,pos);chkmax(ret,f[pos][0]);chkmax(ret,f[pos][1]);
    dfs(co[pos][1],co[pos][1]);chkmax(ret,f[co[pos][1]][0]);chkmax(ret,f[co[pos][1]][1]);
    ans+=ret;return;
}

int main(){
    int n;read(n);
    rep(i,1,n){read(co[i][0]);read(co[i][1]);add_edge(co[i][1],i);}
    
    rep(i,1,n){
        if(!vis[i])dp(i); 
    }
    write(ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Bill_Benation/article/details/86776647