BZOJ1040 [ZJOI2008]骑士(基环树+树形dp)

来源:互联网 发布:常州青蓝软件 编辑:程序博客网 时间:2024/05/13 13:35

【题解】

出现了基环森林,还是考虑断环为链,然后dp

对每个连通块,随意找到环上一边并断开,记该边的端点为x,y
由于x,y的关联断了,所以要限制x,y的情况:
1. x不取,y取上 
2. y不取,x取上 
然后就是树形dp:记 f[i][0]表示不取i时的最大战斗力; f[i][1]表示取i时的最大战斗力,以上两种情况取最优值即可 

注意:
1. 处理破环为链的问题,使用邻接表比较合适 
2. 对x,y作出限制时,若规定取x不取y,实际很难限制y取不取,那就改为规定不取x,y随意即可


【代码】

#include<stdio.h>#include<stdlib.h>#include<string.h>typedef long long LL;LL a[1000005],f[1000005][2];int v[2000005],first[1000005],next[2000005],fan[2000005],vis[2000005];int e=0,U,V,E;LL max(LL a,LL b){if(a>b) return a;return b;}void tj(int x,int y){v[++e]=y;next[e]=first[x];first[x]=e;}void dfs(int x,int Efa){int i;vis[x]=1;for(i=first[x];i!=0;i=next[i])if(i!=Efa&&fan[i]!=Efa){if(vis[v[i]]==1){U=x;V=v[i];E=i;continue;}dfs(v[i],i);}}void solve(int x,int fa,int banE){int i;f[x][1]=a[x];for(i=first[x];i!=0;i=next[i])if(v[i]!=fa&&i!=banE&&fan[i]!=banE){solve(v[i],x,banE);f[x][0]+=max(f[v[i]][0],f[v[i]][1]);f[x][1]+=f[v[i]][0];}}int main(){LL ans=0,t=0;int n,i,x;scanf("%d",&n);for(i=1;i<=n;i++){scanf("%lld%d",&a[i],&x);tj(i,x);tj(x,i);}for(i=1;i<=e;i++){if(i&1) fan[i]=i+1;else fan[i]=i-1;}for(i=1;i<=n;i++)if(vis[i]==0){dfs(i,0);memset(f,0,sizeof(f));solve(U,0,E);t=f[U][0];memset(f,0,sizeof(f));solve(V,0,E);t=max(t,f[V][0]);ans+=t;}printf("%lld",ans);return 0;}


0 0