版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37867156/article/details/82497948
描述
给定一张N个点M条边的有向无环图,分别统计从每个点出发能够到达的点的数量。N,M≤30000。
输入格式
第一行两个整数N,M,接下来M行每行两个整数x,y,表示从x到y的一条有向边。
输出格式
共N行,表示每个点能够到达的点的数量。
样例输入
10 10 3 8 2 3 2 5 5 9 5 9 2 3 3 9 4 8 2 10 4 9
样例输出
1 6 3 3 2 1 1 1 1 1
题解:设从点 x 出发能够到达的点构成的集合是 c(x),从点 x 出发能够到达的点,是从 x 的各个后继节点 y 出发能够到达的点的并集,再加上点 x 自身。先按照拓扑排序算法求出拓扑序,然后按照拓扑序的倒叙进行计算------因为在拓扑序中,任意一条边 (x , y),x 都排在 y 之前。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <queue>
#include <bitset>
#include <algorithm>
using namespace std;
const int maxn = 30000+7;
int head[maxn],ver[maxn];
int Next[maxn];
int tot,cnt;
int deg[maxn],a[maxn];
int n,m;
bitset<maxn> c[maxn];
void add(int x, int y){
ver[++tot] = y, Next[tot] = head[x];
head[x] = tot;
deg[y]++;
}
void toposort(){
queue<int> q;
for(int i = 1; i <= n; i++)
if(deg[i] == 0) q.push(i);
while(q.size()){
int x = q.front();
q.pop();
a[++cnt] = x;
for(int i = head[x]; i; i = Next[i]) {
int y = ver[i];
deg[y]--;
if(deg[y] == 0) q.push(y);
}
}
}
void solve(){
int x, y;
for(int i = cnt; i >= 1; i--) {
x = a[i];
c[x][x] = 1;
for(int j = head[x]; j; j = Next[j]) {
int y = ver[j];
c[x] |= c[y];
}
}
}
int main(){
int x, y;
scanf("%d %d", &n, &m);
while(m--){
scanf("%d %d", &x, &y);
add(x, y);
}
toposort();
solve();
for(int i = 1; i <= n; i++)
printf("%d\n",c[i].count());
return 0;
}