题意:给出有向无环图,若存在一条u->v的有向边,代表u吃v,现在问你去掉一种食物,会有多少动物灭绝。
考虑建反图,就变成了食物支配动物,再建一个虚拟源点,连接所有的入度为0的点,以此开始跑拓扑排序,然后用拓扑序和lca建支配树。
那支配树上的某点能支配的点就是他的子树大小,减去他本身。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
const int maxm=4e5+7;
struct Edge{
int v,next;
}edge[maxm<<1];
//正向图,反向图,支配树;
int head[maxn],headfan[maxn],headzhipei[maxn],top;
int fa[maxn][19];
int xxx;//防越界2333,内存连续,dep[-1]=xxx,这是个小技巧...?
int dep[maxn];
int mi=18;
int du[maxn];
void init(int n){
memset(head,-1,sizeof(head));
memset(headfan,-1,sizeof(headfan));
memset(headzhipei,-1,sizeof(headzhipei));
top=0;
dep[n+1]=1;
}
void add(int u,int v,int head[]){
edge[top].v=v;
edge[top].next=head[u];
head[u]=top++;
}
queue<int> q;
int num;
int t[maxn];//拓扑序;
int n;
void topsort(int st){
num=0;
while(!q.empty()) q.pop();
for(int i=1;i<=n;++i)
if(du[i]==0)
add(st,i,head),add(i,st,headfan),q.push(i);
int u,v;
while(!q.empty()){
u=q.front(); q.pop();
t[++num]=u;
for(int i=head[u];i!=-1;i=edge[i].next){
v=edge[i].v;
--du[v];
if(du[v]==0) q.push(v);
}
}
}
int LCA(int x,int y){
if(x==-1||y==-1) return -1;
if(dep[x]<dep[y]) swap(x,y);
for(int i=mi;i>=0;--i)
if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if(x==y) return x;
for(int i=mi;i>=0;--i)
if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int Size[maxn];
//求支配树上点的子树大小;
void dfs(int u){
Size[u]=1;
int v;
for(int i=headzhipei[u];i!=-1;i=edge[i].next){
v=edge[i].v;
if(v==-1) continue;
dfs(v);
Size[u]+=Size[v];
}
}
int main(){
scanf("%d",&n);
init(n);
int u,v;
for(int i=1;i<=n;++i){
while(1){
scanf("%d",&v);
if(v==0) break;
//i吃v,v支配i;
add(v,i,head);
add(i,v,headfan);
++du[i];
}
}
topsort(n+1);
int lca;
for(int i=1;i<=n;++i){
u=t[i];
lca=-1;
if(headfan[u]!=-1) lca=edge[headfan[u]].v;
for(int j=headfan[u];j!=-1;j=edge[j].next){
v=edge[j].v;
lca=LCA(lca,v);
}
dep[u]=dep[lca]+1;
if(lca!=-1) add(lca,u,headzhipei);
fa[u][0]=lca;
for(int j=1;j<=mi;++j) fa[u][j]=fa[fa[u][j-1]][j-1];
}
dfs(n+1);
for(int i=1;i<=n;++i) printf("%d\n",Size[i]-1);
return 0;
}