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 }