bzoj1040
题解:基环树的例题
先假设没有环,那么用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); }