BZOJ 1040 骑士

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

 

对于100%的测试数据,满足N ≤ 1 000 000,每名骑士的战斗力都是不大于   1 000 000的正整数。

 

Source

与 BZOJ 1023 仙人掌图 很相似,这题同样也是树形dp+环形dp。

如果是一颗树的话,这题就是典型的水题了。f[i][0]表示以i点为根的树且i点不选的最大获益,f[i][1]表示以i点为根的树且i点被选择的最大获益。转移自己脑补一下吧。

如果存在环的话,我们就可以先将树边dp完,再将环单独抠出来dp。

环上dp有特殊处理技巧——断环为链。

我们假定有个环a1,a2,a3...an。将a1与an的边断开,先假定a1选,a1那么不选的代价就是-inf,dp到an,最后的答案就是f[an][0],将答案合并到f[a1]中。再假定a1不选,那么a1选的代价就是-inf,dp到an,最后答案就是min(f[an][0],f[an][1]),合并到f[a1]中。(必须卡死一个状态,不然会多许多非法的转移,也就是我们要dp两次。)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cstring>
 5 using namespace std;
 6 
 7 #define inf (1ll<<60)
 8 #define maxn 1000010
 9 int n,cnt = 1,cir[maxn],w[maxn],fa[maxn],side[maxn];
10 int dfn[maxn],low[maxn],toit[maxn*2],next[maxn*2];
11 long long f[maxn][2],g[maxn][2],ans;
12 
13 inline int read()
14 {
15     int x=0,f=1;char ch=getchar();
16     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
17     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
18     return x*f;
19 }
20 
21 inline void add(int a,int b) { next[++cnt] = side[a]; toit[cnt] = b; side[a] = cnt; } 
22 
23 inline void ins(int a,int b) { add(a,b); add(b,a); }
24 
25 inline void dp(int root,int last)
26 {
27     int nn = 0;
28     while (last != root) cir[++nn] = last,last = fa[last];
29     cir[++nn] = root;
30     for (int i = 1;i <= nn;++i) g[cir[i]][0] = f[cir[i]][0],g[cir[i]][1] = f[cir[i]][1];
31     g[root][1] = inf;
32     for (int i = nn-1;i;--i)
33     {
34         g[cir[i]][0] += min(g[cir[i+1]][0],g[cir[i+1]][1]);
35         g[cir[i]][1] += g[cir[i+1]][0];
36     }
37     f[root][0] = min(g[cir[1]][0],g[cir[1]][1]);
38     for (int i = 1;i <= nn;++i) g[cir[i]][0] = f[cir[i]][0],g[cir[i]][1] = f[cir[i]][1];
39     g[root][0] = inf;
40     for (int i = nn-1;i;--i)
41     {
42         g[cir[i]][0] += min(g[cir[i+1]][0],g[cir[i+1]][1]);
43         g[cir[i]][1] += g[cir[i+1]][0];
44     }
45     f[root][1] = g[cir[1]][0];
46 }
47 
48 inline void dfs(int now)
49 {
50     dfn[now] = low[now] = ++cnt;
51     f[now][0] = w[now];
52     for (int i = side[now];i;i = next[i])
53         if (toit[i] != fa[now])
54         {
55             if (fa[toit[i]] == now) continue;
56             if (!dfn[toit[i]]) fa[toit[i]] = now,dfs(toit[i]);
57             low[now] = min(low[now],low[toit[i]]);
58             if (low[toit[i]] > dfn[now])
59             {
60                 f[now][0] += min(f[toit[i]][0],f[toit[i]][1]);
61                 f[now][1] += f[toit[i]][0];
62             }
63         }
64     for (int i = side[now];i;i = next[i])
65         if (toit[i] != fa[now] && dfn[toit[i]] > dfn[now] && fa[toit[i]] != now)
66             dp(now,toit[i]);
67 }
68 
69 int main()
70 {
71     freopen("1040.in","r",stdin);
72     freopen("1040.out","w",stdout);
73     scanf("%d",&n);
74     for (int i = 1;i <= n;++i) w[i] = read(),ins(read(),i),ans += (long long)w[i];
75     for (int i = 1;i <= n;++i)
76         if (!dfn[i]) cnt = 0,dfs(i),ans -= min(f[i][0],f[i][1]);
77     printf("%lld",ans);
78     fclose(stdin); fclose(stdout);
79     return 0;
80 }
View Code

 

posted @ 2015-02-06 08:43  lmxyy  阅读(312)  评论(0编辑  收藏  举报