bzoj 1040: [ZJOI2008]骑士
题目描述
Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。
最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的Z国又怎能抵挡的住Y国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。
骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出征的。
战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力。
为了描述战斗力,我们将骑士按照1至N编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。
输入输出格式
输入格式:
输入文件knight.in第一行包含一个正整数N,描述骑士团的人数。
接下来N行,每行两个正整数,按顺序描述每一名骑士的战斗力和他最痛恨的骑士。
输出格式:
输出文件knight.out应包含一行,包含一个整数,表示你所选出的骑士军团的战斗力。
输入输出样例
3 10 2 20 3 30 1
30
说明
对于30%的测试数据,满足N ≤ 10;
对于60%的测试数据,满足N ≤ 100;
对于80%的测试数据,满足N ≤ 10 000。
对于100%的测试数据,满足N ≤ 1 000 000,每名骑士的战斗力都是不大于 1 000 000的正整数。
题解:
这题很正常,出的很好........
首先这是必然是个环套树,因为只有且只有一个有仇恨的人,所以必然是有n条边,且父节点唯一
而且这种题目的惯用套路是dp,所以要变成树,所以要删去环上一条边,然后树D,至于删去哪条边,其实是等价的.
需要考虑是否每一种情况都枚举到:
删去一条边后,分别选择边对应的两个节点x,u去做DP,那么我们强制x必须取,那么u就必须不取,u取x就必须不取.
这样是否考虑完全了呢?那么u,x有没有可能都不选呢?显然是可能的,所以我们选择两条边去删就可以包括这种情况了
然而我选两条却WA了两个点,又懒得改,于是改成三条边就过了......(这个不要学)
至于DP,就和 没有上司的舞会相同
定义f[i][0/1]表示i的子树中,是否取i的最大值
显然 : f[i][0]+=max(f[u][0],f[u][1]) u为i的儿子
f[i][1]+=f[u][0]
1 #include <algorithm> 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 using namespace std; 8 typedef long long ll; 9 const int N=1000055; 10 const int inf=1e8; 11 int gi(){ 12 int str=0;char ch=getchar(); 13 while(ch>'9' || ch<'0')ch=getchar(); 14 while(ch>='0' && ch<='9')str=(str<<1)+(str<<3)+ch-48,ch=getchar(); 15 return str; 16 } 17 int n,fa[N],num=1,head[N],nxt[N<<1],to[N<<1],id[N];ll w[N],f[N][2]; 18 bool vis[N];ll ans=0; 19 void addedge(int x,int y){ 20 nxt[++num]=head[x]; 21 to[num]=y; 22 head[x]=num; 23 } 24 int cant=0; 25 void dfs(int x,int last,int lim){ 26 vis[x]=true; 27 int u; 28 f[x][1]=w[x]; 29 for(int i=head[x];i;i=nxt[i]){ 30 u=to[i];if(u==last || i==id[cant] || (i^1)==id[cant])continue; 31 dfs(u,x,lim); 32 f[x][0]+=max(f[u][0],f[u][1]); 33 f[x][1]+=f[u][0]; 34 } 35 if(x==lim)f[x][1]=-inf; 36 } 37 void Clear(){ 38 memset(f,0,sizeof(f)); 39 } 40 void cal(int x){ 41 ll ret=0; 42 while(!vis[x] && fa[x])vis[x]=true,x=fa[x]; 43 for(int i=1;i<=3;i++){ 44 Clear();cant=x;dfs(fa[x],fa[x],x);if(f[fa[x]][1]>ret)ret=f[fa[x]][1]; 45 Clear();cant=fa[x];dfs(x,x,fa[x]);if(f[x][1]>ret)ret=f[x][1]; 46 x=fa[x]; 47 } 48 ans+=ret; 49 } 50 void work() 51 { 52 n=gi(); 53 int y; 54 for(int i=1;i<=n;i++){ 55 w[i]=gi();y=gi(); 56 addedge(i,y);addedge(y,i); 57 fa[i]=y;id[i]=num-1; 58 } 59 for(int i=1;i<=n;i++){ 60 if(!vis[i]) 61 cal(i); 62 } 63 printf("%lld\n",ans); 64 } 65 66 int main() 67 { 68 work(); 69 return 0; 70 }