bzoj1040

CQzhangyu

题解:基环树的例题

先假设没有环,那么用f[i]表示在i的子树中,选i的最大战斗力,g[i]表示在i的子树中,不选i的最大战斗力,然后无脑DP

发现一棵树上只能出现一个环,我们只需要删掉环上的任意一条边即可将环转化为树,那我们只需要人为判断这条边对答案的贡献就行了

设这条边是(u,v),那么有2种情况

1.不选u,那么v选不选都行,以u为根跑一边树形DP就行了

2.不选v,那么u选不选都行,以v为根跑一边树形DP就行了

判环什么的可以用并查集来搞

#include<cstdio>
#include<cctype>
#include<algorithm>
#define maxn 1000002
using namespace std;
int n,cnt,tot,fa[maxn],cb[maxn],to[maxn<<1],nex[maxn<<1],head[maxn],tmp1[maxn],tmp2[maxn];
long long f[maxn],g[maxn],ans;
void read(int &x){
    char ch=getchar();x=0;int f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    x*=f;
}
void read(long long &x){
    char ch=getchar();x=0;int f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    x*=f;
}
void addedge(int u,int v){to[++cnt]=v;nex[cnt]=head[u];head[u]=cnt;}
int findfa(int x){if(fa[x]==x)return x;return fa[x]=findfa(fa[x]);}
void dfs(int x,int fat){
    f[x]=cb[x],g[x]=0;
    for(int i=head[x];i;i=nex[i]){
        if(to[i]==fat)continue;
        dfs(to[i],x);
        g[x]+=max(f[to[i]],g[to[i]]);
        f[x]+=g[to[i]];
    }
}
int main(){
    read(n);int v;
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=n;i++){
        read(cb[i]);read(v);
        if(findfa(i)!=findfa(v)){addedge(i,v);addedge(v,i);fa[fa[i]]=fa[v];}
        else tmp1[++tot]=i,tmp2[tot]=v;
    }
    long long tmp;
    for(int i=1;i<=tot;i++){
        dfs(tmp1[i],0);tmp=g[tmp1[i]];
        dfs(tmp2[i],0);tmp=max(tmp,g[tmp2[i]]);
        ans+=tmp;
    }
    printf("%lld",ans);
}

 

 
posted @ 2018-05-20 15:51  lnyzo  阅读(119)  评论(0编辑  收藏  举报