BZOJ1040: [ZJOI2008]骑士
【传送门:BZOJ1040】
简要题意:
给出n个人,每个人都有自己的战力值和最讨厌的人,选出若干个人使得这些人中的每一个人所讨厌的人都不在这些人中,求出最大战力值
题解:
一看,这好像是森林,再看,还有环!
一开始看,还不会做,结果hanks_o来D飞了我,说这道题很简单
好吧,确实挺简单的。。
将讨厌的人和被讨厌的人连双向边
首先一棵一棵树的求,对于一棵树,先把环给找出来(因为题目中每个人只有一个讨厌的人,所以每棵树中最多只有一个环),然后在环中找一条边将环切断,并且切断的这条边的两点x,y不能同时选择,使得这棵树成为真正意义上的树,然后进行树形DP,对于i点,f[i][0]表示不选第i个点的最大收益,f[i][1]表示选i点的最大收益,然后对于每棵树将x拉到根处,再把y拉到根处,分别求出f[x][1],f[y][1],取最大值,这个最大值就是这棵树的最大收益
然后把每棵树的收益都加起来就是答案
注意:要加long long
UPD(2018.10.5):实际上,这题是基环树森林+DP,但是当时不会,可是操作跟基环树的操作没多大差别
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; typedef long long LL; LL s[1100000]; struct node { int x,y,next,other; }a[2100000];int len,last[1100000]; void ins(int x,int y) { int k1=++len,k2=++len; a[k1].x=x;a[k1].y=y; a[k1].next=last[x];last[x]=k1; a[k2].x=y;a[k2].y=x; a[k2].next=last[y];last[y]=k2; a[k1].other=k2; a[k2].other=k1; } bool v[1100000]; LL f[1100000][2]; int X,Y,K; void bt(int x,int fa) { v[x]=true; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y==fa) continue; if(v[y]==true){X=x;Y=y;K=k;continue;} bt(y,x); } } void dp(int x,int fa) { f[x][0]=0;f[x][1]=s[x]; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y==fa) continue; if(k==K||k==a[K].other) continue; dp(y,x);f[x][0]+=max(f[y][0],f[y][1]);f[x][1]+=f[y][0]; } } int main() { int n; scanf("%d",&n); len=0;memset(last,0,sizeof(last)); for(int i=1;i<=n;i++) { int x; scanf("%d%d",&s[i],&x); ins(i,x); } memset(v,false,sizeof(v)); LL ans=0,t1,t2; memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) { if(v[i]==false) { bt(i,0); dp(X,0);t1=f[X][0]; dp(Y,0);t2=f[Y][0]; ans+=max(t1,t2); } } printf("%lld\n",ans); return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚