普通的树有n个点和n-1条边
如果在这个的基础上添加一条边,就变成了基环树
对它进行dp的时候,考虑删除一条边转换成普通树
对于每一棵环套树,先dfs找环
找到环以后断环为链并将断开的两个点强制其中一个点为根且不选做一次树形DP,对另一个点做同样操作
取两次结果最大值加入ans
BZOJ1040的题意是这样的
每个骑士都有且仅有一个自己最厌恶的骑士
从所有的骑士中选出一个骑士军团,使得军团内没有矛盾的两人
并且,使得这支骑士军团最具有战斗力
第一行包含一个正整数N,描述骑士团的人数。接下来N行,每行两个正整数,按顺序描述每一名骑士的战斗力和他最痛恨的骑士
整个图实际上是一个无向环套树森林
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cmath> 5 #include<cstring> 6 #define N 1000001 7 #define ll long long 8 using namespace std; 9 int n,cnt;ll ans,answer; 10 int v[N],fa[N],head[N],to[N],next[N],ez[N],bh[N]; 11 ll dp[N][2],f[N][4]; 12 bool mark[N]; 13 void Ins(int a,int b) 14 {cnt++;to[cnt]=b;next[cnt]=head[a];head[a]=cnt;} 15 void init() 16 { 17 int i,k; 18 scanf("%d",&n); 19 for(i=1;i<=n;i++) 20 {scanf("%d%d",&v[i],&k);Ins(k,i);fa[i]=k;} 21 } 22 void treedp(int x) 23 { 24 dp[x][1]=v[x];dp[x][0]=0; 25 mark[x]=1; 26 int i=head[x]; 27 while(i>0) 28 { 29 treedp(to[i]); 30 dp[x][0]+=max(dp[to[i]][0],dp[to[i]][1]); 31 dp[x][1]+=dp[to[i]][0]; 32 i=next[i]; 33 } 34 } 35 void finaldp() 36 { 37 int i,k; 38 ans=0; 39 f[1][1]=0;f[1][2]=0; 40 f[1][0]=dp[bh[1]][1]; 41 f[1][3]=dp[bh[1]][0]; 42 for(i=2;i<=bh[0];i++) 43 { 44 k=bh[i]; 45 f[i][0]=f[i-1][1]+dp[k][1]; 46 f[i][1]=max(f[i-1][0],f[i-1][1])+dp[k][0]; 47 f[i][2]=f[i-1][3]+dp[k][1]; 48 f[i][3]=max(f[i-1][2],f[i-1][3])+dp[k][0]; 49 } 50 ans=max(f[bh[0]][1],max(f[bh[0]][2],f[bh[0]][3])); 51 } 52 void solve() 53 { 54 int i,k,j,now; 55 for(i=1;i<=n;i++) 56 { 57 if(mark[i])continue; 58 bh[0]=0; 59 k=i; 60 while(!mark[k]) 61 { 62 mark[k]=1; 63 k=fa[k]; 64 ez[fa[k]]=k; 65 } 66 now=k; 67 while(1) 68 { 69 i=head[k]; 70 dp[k][1]=v[k]; 71 while(i>0) 72 { 73 if(to[i]!=ez[k]) 74 {treedp(to[i]); 75 dp[k][0]+=max(dp[to[i]][0],dp[to[i]][1]); 76 dp[k][1]+=dp[to[i]][0];} 77 i=next[i]; 78 } 79 bh[0]++;bh[bh[0]]=k; 80 k=fa[k]; 81 if(k==now) 82 break; 83 } 84 finaldp(); 85 answer+=ans; 86 } 87 printf("%lld",answer); 88 } 89 int main() 90 { 91 init(); 92 solve(); 93 return 0; 94 }