cychester

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

 

posted on 2018-09-01 12:38  cychester  阅读(395)  评论(1编辑  收藏  举报

导航