bzoj1040[ZJOI2008]骑士——基环树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1040
就是基环树的裸题;
每个骑士有一个憎恨的骑士,也就是n个点n条边,能看出是基环树;
基环树的套路就是把环断开,然后强制不选根节点,树形DP取较大的作为答案。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; int const maxn=1e6+5; int n,m,v[maxn],head[maxn],ct,va[maxn],vb[maxn],fa[maxn]; ll ans,x0[maxn],x1[maxn]; struct N{ int to,next; N(int t=0,int n=0):to(t),next(n) {} }edge[maxn<<1]; void add(int x,int y){edge[++ct]=N(y,head[x]);head[x]=ct;} int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} void dfs(int x,int f) { x0[x]=0;x1[x]=v[x]; for(int i=head[x],u;i;i=edge[i].next) { u=edge[i].to; if(u==f)continue; dfs(u,x); x0[x]+=max(x0[u],x1[u]); x1[x]+=x0[u]; } return; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++)fa[i]=i; for(int i=1,a,b;i<=n;i++) { scanf("%d%d",&v[i],&b); if(find(i)!=find(b)) { fa[find(i)]=find(b); add(i,b);add(b,i); } else va[++m]=i,vb[m]=b;//形成环 } for(int i=1;i<=m;i++)//每棵基环树 { ll t; dfs(va[i],0);t=x0[va[i]]; dfs(vb[i],0);t=max(t,x0[vb[i]]); ans+=t; } printf("%lld",ans); return 0; }