[ZJOI2012] 灾难 题解
爵士好提
Solution
定义\(u\)控制\(v\)当且仅当\(u\)死后\(v\)也会死
把图建出来,从食物向消费者连边
我们不难想到只能先处理食物,再处理消费者,所以先上个拓扑排序
想一想暴力怎么做,对于每个点我们考虑状压维护这个点受哪些点控制,暴力合并即可。
但显然,这个暴力的复杂度是\(O(n^2)\)的。
考虑到控制的一个性质:若\(x\)控制\(z\)而\(y\)同样控制\(z\),则要么\(x\)控制\(y\),要么\(y\)控制\(x\)
我们不妨考虑根据控制的关系建出一棵树来,\(u\)是\(v\)的祖先当且仅当\(u\)控制\(v\),显然,一个点的答案就是这个点在树上对应点节点为根的子树大小\(-1\)
怎么建出这棵树?
接下来的问题就很显然了,我们考虑每遍历到一条边\((u,v)\)都用\(u\)去更新\(v\)的父亲,具体过程用\(\text{LCA}\)算法即可,我使用倍增实现,需要在线维护倍增数组
Code
#include <cstdio>
#include <iostream>
using namespace std;
inline int read() {
int res = 0, flag = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') flag = 1;
for(; isdigit(ch); ch = getchar()) res = (res << 1) + (res << 3) + (ch ^ 48);
if(flag) res = ~res + 1; return res;
}
const int N = 70000;
int n, tail, head, que[N];
int la[N], la1[N], tot, tot1, d[N], fa[N][20], dep[N], sz[N];
struct Edge {int to, nxt;} e[2000010], e1[100000];
inline void build(int u, int v) {e[++tot] = (Edge) {v, la[u]}, la[u] = tot, ++d[v];}
inline void build1(int u, int v) {e1[++tot1] = (Edge) {v, la1[u]}, la1[u] = tot1;}
void update(int x) {
if(x == n + 1) return ;
build1(fa[x][0], x), dep[x] = dep[fa[x][0]] + 1;
for(int i = 1; i <= 17; ++i)
fa[x][i] = fa[fa[x][i - 1]][i - 1];
return ;
}
inline int getlca(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
for(int i = 17; i + 1; --i)
if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
if(x == y) return x;
for(int i = 17; i + 1; --i)
if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
void work(int x, int y) {fa[x][0] = (fa[x][0] ? getlca(fa[x][0], y) : y);}
void dfs(int k) {
sz[k] = 1;
for(int v, i = la1[k]; i; i = e1[i].nxt) {
v = e1[i].to;
dfs(v), sz[k] += sz[v];
}
return ;
}
void solve() {
que[tail = 1] = n + 1, dep[n + 1] = 1;
while(++head <= tail) {
update(que[head]);
for(int v, i = la[que[head]]; i; i = e[i].nxt) {
v = e[i].to;
--d[v], work(v, que[head]);
if(!d[v]) que[++tail] = v;
}
}
dfs(n + 1);
}
int main() {
n = read();
for(int v, u = 1; u <= n; ++u) {
v = read();
while(v) build(v, u), v = read();
}
for(int i = 1; i <= n; ++i) if(!d[i]) build(n + 1, i);
solve();
for(int i = 1; i <= n; ++i) printf("%d\n",sz[i] - 1);
}