bzoj1040(ZJOI2008)骑士——基环树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1040
基环树的模板。
套路就是把环断开,先把一端作为根节点,强制不选;再把另一端作为根节点,强制不选。
人家的这个判断环的方法真好!还顺便没有连上环的那条边,省下了在函数里判断。
别忘了有多棵基环树!
#include<iostream> #include<cstdio> #include<cstring> #define ll long long using namespace std; const int N=1e6+5; int n,m,head[N],xnt,fa[N],rta[N],rtb[N]; ll dp[N][2],a[N],ans; struct Edge{ int next,to; Edge(int n=0,int t=0):next(n),to(t) {} }edge[N<<1]; int find(int a){return fa[a]==a?a:fa[a]=find(fa[a]);} void add(int a,int b) { edge[++xnt]=Edge(head[a],b);head[a]=xnt; edge[++xnt]=Edge(head[b],a);head[b]=xnt; fa[find(a)]=find(b); } void sol(int cr,int fa) { dp[cr][1]=a[cr];dp[cr][0]=0; for(int i=head[cr],v;i;i=edge[i].next) { v=edge[i].to; if(v==fa)continue; sol(v,cr); dp[cr][0]+=max(dp[v][0],dp[v][1]); dp[cr][1]+=dp[v][0]; } } int main() { scanf("%d",&n);int tmp; for(int i=1;i<=n;i++)fa[i]=i; for(int i=1;i<=n;i++) { scanf("%lld%d",&a[i],&tmp); if(find(i)!=find(tmp)) add(i,tmp); else rta[++m]=i,rtb[m]=tmp; } ll c; for(int i=1;i<=m;i++) { sol(rta[i],0);c=dp[rta[i]][0]; sol(rtb[i],0);c=max(c,dp[rtb[i]][0]); ans+=c; } printf("%lld",ans); return 0; }