可达性统计 题目地址(牛客)
一道比较经典的拓扑排序题
题目描述
给定一张\(N\)个点\(M\)条边的有向无环图,分别统计从每个点出发能够到达的点的数量。\(N,M≤30000\)。
题解
设从点 u 出发能够到达的点构成的集合是 f(u),从点 u 出发能够到达的点,是从 u 的各个后继节点 v 出发能够到达的点的并集,再加上点 u 自身。先按照拓扑排序算法求出拓扑序,然后按照拓扑序的倒叙进行计算------因为在拓扑序中,任意一条边 (u ,v),u 都排在 v 之前。倒序处理即可。
Code
#include<algorithm> #include<iostream> #include<cstdio> #include<bitset> #include<cmath> #include<queue> #define N 30007 using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); } return x * f; } int n,m,cnt,tot; int head[N],du[N],tp[N]; //tp拓扑序数组 struct Edge { int next,to; }edge[N<<1]; inline void add(int u,int v) { edge[++cnt].next = head[u]; edge[cnt].to = v; head[u] = cnt; } void topo() { queue<int> q; for(int i=1;i<=n;++i) if(!du[i]) q.push(i); while(!q.empty()) { int u = q.front(); q.pop(); tp[++tot] = u; for(int i=head[u];i;i=edge[i].next) { int v = edge[i].to; du[v]--; if(!du[v]) q.push(v); } } } int main() { n = read() ,m = read(); for(int i=1,u,v;i<=m;++i) { u = read() ,v = read(); add(u,v); du[v]++; } topo(); bitset<N> f[N]; for(int i=cnt;i>=1;--i) { int u = tp[i]; f[u][u] = 1; for(int j=head[u];j;j=edge[j].next) { int v = edge[j].to; f[u] |= f[v]; //只要有就继承一下儿子 } } for(int i=1;i<=n;++i) printf("%d\n",f[i].count()); return 0; }