[ZJOI2008] 骑士
题目描述
Z 国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。
最近发生了一件可怕的事情,邪恶的 Y 国发动了一场针对 Z 国的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的 Z 国又怎能抵挡的住 Y 国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。
骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出征的。
战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力。
为了描述战斗力,我们将骑士按照 1 1 1 至 n n n 编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。
输入格式
第一行包含一个整数 n n n,描述骑士团的人数。
接下来 n n n 行,每行两个整数,按顺序描述每一名骑士的战斗力和他最痛恨的骑士。
输出格式
应输出一行,包含一个整数,表示你所选出的骑士军团的战斗力。
样例 #1
样例输入 #1
3
10 2
20 3
30 1
样例输出 #1
30
提示
数据规模与约定
对于 30 % 30\% 30% 的测试数据,满足 n ≤ 10 n \le 10 n≤10;
对于 60 % 60\% 60% 的测试数据,满足 n ≤ 100 n \le 100 n≤100;
对于 80 % 80\% 80% 的测试数据,满足 n ≤ 1 0 4 n \le 10 ^4 n≤104。
对于 100 % 100\% 100% 的测试数据,满足 1 ≤ n ≤ 1 0 6 1\le n \le 10^6 1≤n≤106,每名骑士的战斗力都是不大于 1 0 6 10^6 106 的正整数。
思路
这道题就是很典型的基环树,为什么是基环树呢?因为一个点对应一条边(因为不存在没有矛盾的两人)。
基环树dp的操作:它相比于仙人掌树,它的环只有一个,此时我们可以用栈来找它的环,而仙人掌树必须是用targin的改进版(无需缩点的)。
然后基环树有环,因此得破一个点,然后我们就可以操作了:
我们设 f[u][0/1] 表示以 u 为根节点的子树中,根节点选或不选的方案数集合
。
状态转移就跟 没有上司的舞会 那道题类似。
具体代码:
代码
//基环树dp
//f[u][0/1]表示以i的子树中,0表示选,1表示不选的最大战斗力
#include<iostream>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
const int N = 1e6+10,M = 2*N,INT = 1e18;
int e[M],ne[M],h[N],idx;
int w[N];
bool ins[N],st[N];
int stk[N];//基环树是用栈来实现找环的
int f1[N][2],f2[N][2];//f1为删除前,f2为删后
int n;
int rm[N],ans;
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs_f(int u,int ap,int f[][2]){
//no u
for(int i=h[u];~i;i=ne[i]){
if(rm[i])continue;
int j=e[i];
dfs_f(j,ap,f);
f[u][0]+=max(f[j][0],f[j][1]);
}
// yes u
f[u][1]=-INT;
if(u!=ap){
f[u][1]=w[u];
for(int i=h[u];~i;i=ne[i]){
if(rm[i])continue;
int j=e[i];
f[u][1]+=f[j][0];
}
}
}
void dfs_c(int u,int from){
st[u]=ins[u]=true;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(!st[j])dfs_c(j,i);//继续找环
else if(ins[j]){
rm[i]=1;//将 u -> j 的边删掉
dfs_f(j,-1,f1);//j不选
dfs_f(j,u,f2);//j选
ans+=max(f1[j][0],f2[j][1]);
}
}
ins[u]=false;
}
signed main(){
cin>>n;
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++){
int x;
cin>>w[i]>>x;
add(x,i);
}
for(int i=1;i<=n;i++){
if(!st[i]){
dfs_c(i,-1);
}
}
cout<<ans;
return 0;
}