\(\\\)
Description
给出一个食物网,每个生物指向的生物都是它可以捕食的对象,保证是图是DAG。
如果一个捕食者的所有捕食对象都灭绝了,那么它们也会灭绝。
求每一个动物灭绝之后,有多少个动物会随之灭绝。
- \(n\le 65534\)
Solution
考察建图思维的好题。
如果我们按照 捕食者\(\to\)捕食对象 的关系连边,那么我们得到的是一张捕食关系的流向图,无法判断反向的影响。
所以要按照 捕食对象\(\to\)捕食者 的关系连边,得到的是一张能量供应的流向图。
那么形象的说,每个点都把能量流向出边指向的点,如果一个点消失了,从他这里流出去的所有能量全都消失。
如果这个时候某些点没有能量流入,那么这些点就是需要统计到消失点的答案里。
但是只有这些吗?并不是。因为这些受影响消失的点可能继续导致一些点消失,后面的答案我们没有统计。
我们设 \(die_x\) 表示节点 x 如果消失,有连边的点直接消失的点的个数。
那么我们一个点要累加的,就是所有直接指向的消失的点的个数+这些点的 \(die\) 值。
考虑如何求出 \(die\) 值。我们发现这就是能量供应关系的图的拓扑序。
我们新建一棵树形结构,父子关系表示,如果父节点消失,子节点必然消失。
首先将这张能量供应图中所有入度为 \(0\) 的点连向虚拟根节点,代表他们不会因为别的生物灭亡而灭亡。
然后考虑拓扑到一个点时,它应该作为哪个节点的子节点。显然是它的所有食物在这棵树上的 \(LCA\)。
因为拓扑,所以他的所有食物必然已经在这个树上,我们两两合并求 \(LCA\) 即可。
因为要找所有在能量供给图上指向当前点的点,所以我们还要把捕食的图建出来,便于找食物有哪些。
因为求 \(LCA\) 多次,需要 \(log\) 的复杂度,因为树的形态是变化的,所以链剖和离线算法显然都不行。
考虑倍增。查找两两合并是没有问题了,但是新插入节点怎么办?
注意到倍增数组的构造只需要知道父节点,所以在把拓扑到的点插入树的同时构造一下倍增数组就好了。
因为这个树的定义,每一个点的答案显然是子树大小 \(-1\) 。
Code
一直 \(90\) 不知道为什么,后来发现........
在 \(LCA\) 的时候根节点设成了 \(n+1\) ,但是如果倍增的时候深的跳超了,但是另一个是根节点,根据判断条件并没有跳超深度,它就会跳到 0 号节点然后 GG....
给 \(n+1\) 号节点设个深度,或者根节点换成 \(0\) 号节点就好啦
#include<cmath>
#include<queue>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 70000
#define R register
#define gc getchar
using namespace std;inline int rd(){int x=0; bool f=0; char c=gc();while(!isdigit(c)){if(c=='-')f=1;c=gc();}while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}return f?-x:x;
}int n,m,tot,hd[N],deg[N],tot2,hd2[N];int t,tot1,d[N],hd1[N],f[N][20],ans[N];struct edge{int to,nxt;}e[N<<3],e1[N<<1],e2[N<<3];inline void add(int u,int v){e[++tot].to=v; e[tot].nxt=hd[u]; hd[u]=tot;
}inline void add1(int u,int v){e1[++tot1].to=v; e1[tot1].nxt=hd1[u]; hd1[u]=tot1;
}inline void add2(int u,int v){e2[++tot2].to=v; e2[tot2].nxt=hd2[u]; hd2[u]=tot2;
}inline void getfa(int u,int fa){f[u][0]=fa; d[u]=d[fa]+1;for(R int i=1;i<=t;++i) f[u][i]=f[f[u][i-1]][i-1];
}inline int lca(int u,int v){if(d[u]>d[v]) u^=v^=u^=v;for(R int i=t;~i;--i) if(d[f[v][i]]>=d[u]) v=f[v][i];if(u==v) return u;for(R int i=t;~i;--i)if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];return f[u][0];
}inline void work(int u){int res=e2[hd2[u]].to;for(R int i=e2[hd2[u]].nxt;i;i=e2[i].nxt) res=lca(res,e2[i].to);getfa(u,res); add1(u,res); add1(res,u);
}void dfs(int u,int fa){for(R int i=hd1[u],v;i;i=e1[i].nxt)if((v=e1[i].to)!=fa){dfs(v,u); ans[u]+=ans[v];}
}queue<int> q;int main(){t=log2(n=rd())+1;for(R int i=1,x;i<=n;++i){x=rd();while(x){++deg[i];add(x,i);add2(i,x);x=rd();}ans[i]=1;}for(R int i=1;i<=n;++i)if(!deg[i]){q.push(i); getfa(i,0);add1(i,0); add1(0,i);}while(!q.empty()){int u=q.front(); q.pop();for(R int i=hd[u],v;i;i=e[i].nxt)if((--deg[v=e[i].to])==0) q.push(v),work(v);}dfs(0,-1);for(R int i=1;i<=n;++i) printf("%d\n",ans[i]-1);return 0;
}