bzoj1040: [ZJOI2008]骑士

1040: [ZJOI2008]骑士

Time Limit: 10 Sec  Memory Limit: 162 MB

Description

  Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各
界的赞扬。最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争。战火绵延五百里,在和平环境
中安逸了数百年的Z国又怎能抵挡的住Y国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一
个真龙天子的降生,带领正义打败邪恶。骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一
些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出
征的。战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有
的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的
情况),并且,使得这支骑士军团最具有战斗力。为了描述战斗力,我们将骑士按照1至N编号,给每名骑士一个战
斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。

Input

  第一行包含一个正整数N,描述骑士团的人数。接下来N行,每行两个正整数,按顺序描述每一名骑士的战斗力
和他最痛恨的骑士。

Output

  应包含一行,包含一个整数,表示你所选出的骑士军团的战斗力。

Sample Input

3
10 2
20 3
30 1

Sample Output

30

HINT

 

N ≤ 1 000 000,每名骑士的战斗力都是不大于 1 000 000的正整数。

 

Source

 

首先想到将互相矛盾的两名骑士之间连边,若图中没有环,那么就是“没有上司的舞会”模板题(建议先做这个);

因为每名骑士只有一个与他矛盾的士兵,也就是说一个点只有一条出边,那么我们可以证明一个联通块中至多只会出现一个环(证明略);

有环该怎么做呢?取一条环上的任意一条边(为什么?请读者自行思考(o(╥﹏╥)o其实是我不会)),将其删去,对删去的边的两个端点分别为根进行一次dp(dp时根节点不取到);

判环可以用DFS;

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #define MAXN 2000008
 6 using namespace std;
 7  
 8 int n,m,tot,cnt,fa[MAXN],head[MAXN],next[MAXN],vet[MAXN],a[MAXN],q[MAXN][2];
 9 long long ans,res,dp[MAXN][2];
10  
11 inline int read(){
12     char ch=getchar(); int f=1,x=0;
13     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
14     while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
15     return x*f;
16 }
17  
18 void add(int x,int y){
19     tot++;
20     next[tot]=head[x];
21     head[x]=tot;
22     vet[tot]=y;
23 }
24  
25 int find(int x){
26     if(fa[x]==x) return x;
27     return fa[x]=find(fa[x]);
28 }
29  
30 void merge(int aa,int bb){
31     int ii=find(aa);
32     int jj=find(bb);
33     if(ii!=jj){
34         add(aa,bb);
35         add(bb,aa);
36         fa[jj]=ii;
37     }
38     else{
39         q[++cnt][0]=aa;
40         q[cnt][1]=bb;
41     }
42          
43 }
44  
45 void dfs(int u,int fa){
46     dp[u][1]=0; dp[u][0]=0;
47     for(int i=head[u];i;i=next[i]){
48         int y=vet[i];
49         if(y==fa) continue;
50         dfs(y,u);
51         dp[u][0]+=max(dp[y][1],dp[y][0]);
52         dp[u][1]+=dp[y][0];
53     }
54     dp[u][1]+=a[u];
55 }
56  
57 int main(){
58     n=read(); cnt=0; tot=0;
59     for(int i=1;i<=n;i++) fa[i]=i;
60     for(int i=1;i<=n;i++){
61         int x;
62         a[i]=read(); x=read();
63         merge(i,x);
64     }
65     ans=0; res=0;
66     for(int i=1;i<=cnt;i++){
67         int x=q[i][0],y=q[i][1];
68         dfs(x,-1); res=dp[x][0];
69         dfs(y,-1); ans+=max(res,dp[y][0]);
70     }
71     printf("%lld",ans);
72 }

 

转载于:https://www.cnblogs.com/WQHui/p/8570125.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值