[BZOJ1040][ZJOI2008]骑士 基环树DP

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1040

题目给出了$n$个点和$n$条无向边,即一棵基环树或者基环树森林。

如果题目给的关系是在一棵树上,就是一道经典的树形DP。现在我们考虑转化一下。

我们先找到那个环上的任意一条边,端点为u,v。加上这条边的影响仅仅是不能同时选择u和v。

所以我们考虑去掉这条边再分类讨论。如果不选u,那么v任意选;如果不选v,那么u任意选。那么DP的时候强制不走这条边,同时取两种不选根节点的最大值作为答案就行了。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 int inline readint(){
 7     int Num;char ch;
 8     while((ch=getchar())<'0'||ch>'9');Num=ch-'0';
 9     while((ch=getchar())>='0'&&ch<='9') Num=Num*10+ch-'0';
10     return Num;
11 }
12 int N,val[1000010];
13 int to[2000010],ne[2000010],fir[1000010],cnt=-1;
14 void add(int a,int b){
15     to[++cnt]=b;
16     ne[cnt]=fir[a];
17     fir[a]=cnt;
18 }
19 bool vis[1000010];
20 int U,V,E;
21 void Dfs(int x,int fa){
22     vis[x]=true;
23     for(int i=fir[x];i!=-1;i=ne[i]){
24         int v=to[i];
25         if(v!=fa){
26             if(vis[v]){
27                 U=x;
28                 V=v;
29                 E=i;
30             }
31             else Dfs(v,x);
32         }
33     }
34 }
35 ll f[1000010],g[1000010];
36 void Dp(int x,int fa){
37     f[x]=val[x];
38     g[x]=0;
39     for(int i=fir[x];i!=-1;i=ne[i]){
40         int v=to[i];
41         if(i==E||(i^1)==E||v==fa) continue;
42         Dp(v,x);
43         f[x]+=g[v];
44         g[x]+=max(f[v],g[v]);
45     }
46 }
47 int main(){
48     memset(fir,-1,sizeof(fir));
49     N=readint();
50     for(int i=1;i<=N;i++){
51         val[i]=readint();
52         int tmp=readint();
53         add(i,tmp);
54         add(tmp,i);
55     }
56     ll ans=0;
57     for(int i=1;i<=N;i++){
58         if(!vis[i]){
59             Dfs(i,0);
60             Dp(U,0);
61             ll tmp=g[U];
62             Dp(V,0);
63             tmp=max(tmp,g[V]);
64             ans+=tmp;
65         }
66     }
67     printf("%lld\n",ans);
68     return 0;
69 }

 

posted @ 2017-10-09 21:46  halfrot  阅读(160)  评论(0编辑  收藏  举报