tyvj 创世纪 - 基环树

codevs :   传送门

 Description

  上帝手中有着N 种被称作“世界元素”的东西,现在他要把它们中的一部分投放到一个新的空间中去以建造世界。每种世界元素都可以限制另外一种世界元素,所以说上帝希望所有被投放的世界元素都有至少一个没有被投放的世界元素能够限制它,这样上帝就可以保持对世界的控制。由于那个著名的有关于上帝能不能制造一块连自己都不能举起的大石头的二律背反命题,我们知道上帝不是万能的,而且不但不是万能的,他甚至有事情需要找你帮忙——上帝希望知道他最多可以投放多少种世界元素,但是他只会O(2N) 级别的算法。虽然上帝拥有无限多的时间,但是他也是个急性子。你需要帮助上帝解决这个问题。

题解

把$A[ i ]$ 当作 $f[ i ]$  即父节点

dfs找环 + 断环 + 重连

只需找到环上的任意两个点记录即可

找到了$x, y$ , 并且$A_x = y$

断环: 让找到的两个点之间的边断开, 从子节点开始$dp$, 算出投放 $x$时的最大值即 $f[x][1]$

重连: 当然不可能真的重连,只需要让 $x$ 不被投放, 并且在处理$y$时, $y$的子节点无需限制它, 因为已经有$x$在限制了

 相当于重连

记得手工栈, 不然会RE

注意一个点组成的环需要一些特判,不然会WA

代码

  1 #include<cstring>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #define ll long long
  5 #define rd read()
  6 #define rep(i,a,b) for(register int i = (a); i <= (b); ++i)
  7 #define per(i,a,b) for(register int i = (a); i >= (b); --i)
  8 #define R register
  9 using namespace std;
 10 
 11 const int N = 1e6 + 1e3;
 12 
 13 int n, m, fa[N], vis[N];
 14 int f[N][2], pos1, pos2, eg;
 15 int tot, head[N];
 16 
 17 struct edge {
 18     int to, nxt;
 19 }e[N << 1];
 20 
 21 int read() {
 22     int X = 0, p = 1; char c = getchar();
 23     for(; c > '9' || c < '0'; c = getchar()) if( c== '-') p = -1;
 24     for(; c >= '0' && c <= '9'; c = getchar()) X = X * 10 + c - '0';
 25     return X * p;
 26 }
 27 
 28 void add(int u, int v) {
 29     e[++tot].to = v;
 30     e[tot].nxt = head[u];
 31     head[u] = tot;
 32 }
 33 
 34 int lev;
 35 int st_x[N];
 36 int st_nt[N];
 37 int st_tmp[N];
 38 
 39 #define x st_x[lev]
 40 #define nt st_nt[lev]
 41 #define tmp st_tmp[lev]
 42 
 43 void find_cir(int u) {
 44     st_x[1] = u;
 45     lev = 1;
 46 start:;
 47     vis[x] = 1;
 48     nt = fa[x];
 49     if(vis[nt]) {
 50         pos1 = x; pos2 = nt;
 51     }
 52     else {
 53         st_x[lev + 1] = nt;
 54         lev++;
 55         goto start;
 56     }
 57 end:;
 58     if((--lev)) goto end;
 59 }
 60 
 61 
 62 int st_i[N];
 63 
 64 #define i st_i[lev]
 65 
 66 void dp(int u) {
 67     st_x[1] = u;
 68     lev = 1;
 69 start:;
 70     tmp = 0;
 71     f[x][1] = f[x][0] = 0;
 72     vis[x] = 1;
 73     for(i = head[x]; i; i = e[i].nxt) {
 74         nt = e[i].to;
 75         if(nt == pos1) continue;
 76         st_x[lev + 1] = nt;
 77         lev++;
 78         goto start;
 79 end:;
 80 
 81         f[x][0] = f[x][0] + max(f[nt][0], f[nt][1]);
 82         tmp += max(f[nt][1], f[nt][0]);
 83     }
 84     for(i = head[x]; i; i = e[i].nxt) {
 85         nt = e[i].to;
 86         if(nt == pos1) continue;
 87         f[x][1] = max(f[x][1], 1 + tmp - max(f[nt][0], f[nt][1]) + f[nt][0]);
 88     }
 89     if(--lev) goto end;
 90 }
 91 
 92 void dp2(int u) {
 93     st_x[1] = u;
 94     lev = 1;
 95 start:;
 96     tmp = 0;
 97     f[x][1] = f[x][0] = 0;
 98     for(i = head[x]; i; i = e[i].nxt) {
 99         nt = e[i].to;
100         if(nt == pos1) continue;
101         st_x[lev + 1] = nt;
102         lev++;
103         goto start;
104 end:;
105 
106         f[x][0] = f[x][0] + max(f[nt][0], f[nt][1]);
107         if(x == pos2 && pos1 != pos2) f[x][1] = f[x][1] + max(f[nt][0], f[nt][1]);
108         tmp += max(f[nt][1], f[nt][0]);
109     }
110     if(x == pos2 && pos1 != pos2) {
111         f[x][1]++;
112         if(--lev) goto end;
113     }    
114     for(i = head[x]; i; i = e[i].nxt) {
115         nt = e[i].to;
116         f[x][1] = max(f[x][1], 1 + tmp - max(f[nt][0], f[nt][1]) + f[nt][0]);
117     }
118     if(--lev) goto end;
119 }
120 
121 #undef x
122 #undef i
123 #undef nt
124 #undef tmp
125 
126 int work(int x)  {
127     int maxn = 0;
128     find_cir(x);
129     dp(pos1);
130     maxn = f[pos1][1];
131     dp2(pos1);
132     maxn = max(maxn, f[pos1][0]);
133     return maxn;
134 }
135 
136 int main()
137 {
138     n = rd;
139     int ans = 0;
140     rep(i, 1, n) fa[i] = rd, add(fa[i], i);
141     rep(i, 1, n) if(!vis[i]) ans += work(i);
142     printf("%d\n", ans);
143 }
View Code

猜你喜欢

转载自www.cnblogs.com/cychester/p/9569915.html
今日推荐