正题
题目链接:https://www.luogu.com.cn/problem/P2597
题目大意
nnn个点的一张DAGDAGDAG,对于每个点xxx求有多少点yyy满足从yyy出发到达某个出度为000的所有路径都必须经过xxx。
解题思路
首先建立一张反图,然后一个超级源点连向所有入度为000的节点。问题就变为了求每个点支配了多少个节点。
考虑如何求DAGDAGDAG的支配树,一个比较显然的结论就是支配树上一个节点的父节点就是图上所有直接连向它这个点的点在支配树上的LCALCALCA。所以拓扑排序的时候动态维护倍增数组求LCALCALCA即可。
时间复杂度O(mlogn+n)O(m\log n+n)O(mlogn+n)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1e5+10,T=18;
struct node{int to,next;
}a[N*12];
int n,tot,cnt,ls[N],top[N],in[N],dep[N],f[N][T+1],siz[N];
queue<int> q;
void addl(int x,int y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;in[y]++;return;
}
int lca(int x,int y){if(dep[x]>dep[y])swap(x,y);for(int i=T;i>=0;i--)if(dep[f[y][i]]>=dep[x])y=f[y][i];if(x==y)return x;for(int i=T;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];return f[x][0];
}
void solve(){q.push(n);while(!q.empty()){int x=q.front();top[++cnt]=x;q.pop();dep[x]=dep[f[x][0]]+1;for(int i=1;i<=T;i++)f[x][i]=f[f[x][i-1]][i-1];for(int i=ls[x];i;i=a[i].next){int y=a[i].to;in[y]--;if(f[y][0])f[y][0]=lca(f[y][0],x);else f[y][0]=x;if(!in[y])q.push(y);}}return;
}
int main(){scanf("%d",&n);n++;for(int i=1;i<n;i++){int x;while(1){scanf("%d",&x);if(!x)break;addl(x,i);}}for(int i=1;i<n;i++)if(!in[i])addl(n,i);solve();for(int i=n;i>=1;i--)siz[top[i]]++,siz[f[top[i]][0]]+=siz[top[i]];for(int i=1;i<n;i++)printf("%d\n",siz[i]-1);return 0;
}