[ZJOI2008]骑士 DP dfs
题解:
观察题面可以很快发现这是一棵基环内向树(然而并没有什么用。。。)
再稍微思考一下,假设将这个环中的任意一点设为root,然后去掉root到下面的特殊边(即构成环的那条边),那么就构成了一棵树,并且可以用简单树形DP解决。
再考虑加上这条边的限制,设被去掉的这条边是连接root 和 x的, 这条边实际上就是限制了在选root的时候不能选x,那么考虑一个暴力的想法。
我们先在图中dfs,找到这个环,然后任意指定一点为root,再跑两边树形DP,一遍强制不选root,然后跑普通树形DP。另一遍强制选root,然后跑树形DP加上特判不能选x,最后两种答案取max即可。
我这样写可能比较长,别人的做法只要写两遍dfs,我需要3遍(不过后面两个dfs可以复制粘贴)。但实际上是一个思路。别人的做法是dfs找到这个环,然后随便找条环上的边断开,然后强制这条边连接的两个点其中一个不选(其实就和我的写法一样的意思,只不过我是先把这两个点中的一个指定为了root)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 1000100 5 #define ac 2000100 6 #define LL long long 7 int n, m, root; 8 int s[AC], p[AC], deep[AC]; 9 LL f[AC][2], g[AC][2], ans;//存下每个点的厌恶对象以方便判断 10 int Head[AC], date[ac], Next[ac], tot; 11 bool z[AC], vis[AC], book[AC], flag; 12 13 inline int read() 14 { 15 int x = 0;char c = getchar(); 16 while(c > '9' || c < '0') c = getchar(); 17 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 18 return x; 19 } 20 21 inline void add(int f, int w) 22 { 23 date[++tot] = w, Next[tot] = Head[f], Head[f] = tot; 24 date[++tot] = f, Next[tot] = Head[w], Head[w] = tot; 25 } 26 27 void pre() 28 { 29 n = read(); 30 for(R i = 1; i <= n; i ++) 31 { 32 s[i] = read(), p[i] = read(); 33 add(i, p[i]); 34 } 35 deep[1] = 1; 36 } 37 38 void dfs1(int x)//找到反向边的那个点定为root 39 { 40 z[x] = true; 41 int now; 42 //if(root) return ; 43 for(R i = Head[x]; i; i = Next[i]) 44 { 45 now = date[i]; 46 if(z[now] && deep[now] + 1 != deep[x]) root = x; 47 if(z[now] && p[now] == x && p[x] == now) root = x, flag = true;//特殊情况 48 //if(root) return ; 49 if(z[now]) continue; 50 deep[now] = deep[x] + 1; 51 dfs1(now); 52 } 53 } 54 55 void dfs2(int x)//强制选root 56 { 57 int now; 58 vis[x] = true; 59 f[x][1] = s[x];//初始化 60 for(R i = Head[x]; i; i = Next[i]) 61 { 62 now = date[i]; 63 if(vis[now]) continue; 64 if(now == p[x] && x == root && !flag) continue; 65 dfs2(now);//忽略这条边,如果是特殊情况则不能忽略,因为是重边,一旦忽略将忽略2条 66 f[x][1] += f[now][0]; 67 f[x][0] += max(f[now][0], f[now][1]); 68 } 69 if(x == p[root]) f[x][1] = f[x][0]; 70 } 71 72 void dfs3(int x)//强制不选root 73 { 74 int now; 75 g[x][1] = s[x]; 76 book[x] = true; 77 for(R i = Head[x]; i; i = Next[i]) 78 { 79 now = date[i]; 80 if(book[now]) continue; 81 if(now == p[x] && x == root && !flag) continue; 82 dfs3(now); 83 g[x][1] += g[now][0]; 84 g[x][0] += max(g[now][0], g[now][1]); 85 } 86 } 87 88 void work() 89 { 90 for(R i = 1; i <= n; i ++)//因为本来就不一定联通,所以要跑多次 91 if(!z[i]) 92 { 93 dfs1(i); 94 dfs2(root); 95 dfs3(root); 96 ans += max(f[root][1], g[root][0]); 97 } 98 printf("%lld\n", ans); 99 } 100 int main() 101 { 102 // freopen("in.in", "r", stdin); 103 pre(); 104 work(); 105 // fclose(stdin); 106 return 0; 107 }