P2607 [ZJOI2008]骑士
题解:
早在寒假就讲了这题了,现在才A掉。话说如果题目上给出的关系就是一棵树的话,显然就是没有上司的舞会。但我们看样例就不是一棵树,而是成环的关系。不过也无妨,我们遇到环时同样可以解决,你在dfs找环的时候就可以记录一下任意两个在环上的点,然后把环断开,以这两个点为根分别跑树形dp。树形dp的状态定义也很明显:设dp[i][0/1]表示以i为根,选或不选它本身所获得的最大值。转移方程就呼之欲出了:dp[i][0]+=max(dp[son[i][1],dp[son[i]][0])。加上它的儿子选或不选的最大值。dp[i][1]+=dp[son[i]][0]。他选了他的儿子们都不能选。最后每个连通块内加上的都是dp[root][0]的最大值。
代码如下:
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+7; int n; int x,power[maxn]; struct node{ int nxt,to; }edge[maxn*2]; int cnt=1,head[maxn]; long long dp[maxn][2]; void add(int x,int y){ edge[++cnt].nxt=head[x]; edge[cnt].to=y; head[x]=cnt; } bool vis[maxn]; int edge_num,point_num1,point_num2; long long sum; void circle(int x,int f){ vis[x]=true; for(int i=head[x];i;i=edge[i].nxt){ int v=edge[i].to; if(v==f) continue; if(!vis[v]) circle(v,x); else{ edge_num=i; point_num1=x;//记录在环上两点的信息 point_num2=v; } } } void dfs(int x,int f){ dp[x][1]=power[x]; dp[x][0]=0; for(int i=head[x];i;i=edge[i].nxt){ int v=edge[i].to; if(v==f) continue; if(i==edge_num||i==(edge_num^1)) continue; dfs(v,x); dp[x][0]+=max(dp[v][0],dp[v][1]); dp[x][1]+=dp[v][0]; } } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d",&power[i],&x); add(i,x);add(x,i); } for(int i=1;i<=n;i++){ if(!vis[i]){ circle(i,0); dfs(point_num1,0); long long ans=dp[point_num1][0]; dfs(point_num2,0); sum+=max(ans,dp[point_num2][0]); } } printf("%lld\n",sum); return 0; }