给定一张N个点M条边的有向无环图,分别统计从每个点出发能够到达的点的数量。N,M≤30000。
输入格式
第一行两个整数N,M,接下来M行每行两个整数x,y,表示从x到y的一条有向边。
输出格式
共N行,表示每个点能够到达的点的数量。
题解:从点x出发能够到达的点构成的集合是f(x),有:
f(x)={x}U(U存在有向边(x,y) f(y)),
从x出发能够到达的点,是从“x的各个后继点y”出发能够到达的点的并集,再并上x自身。可以按照拓扑序的倒序进行计算。集合的并集计算可以利用状态压缩,用一个N位的二进制数表示可达的节点,bitset做一个或运算即可。
#include <iostream>
#include <bitset>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn = 30000+5;
int n,m,cnt=0;
int n1,n2;
bitset<maxn> bs[maxn];
int ver[maxn],Next[maxn],head[maxn],deg[maxn],a[maxn];
int tot=0;
void add(int x,int y){
ver[++tot]=y,Next[tot]=head[x],head[x]=tot;
deg[y]++;
}
void topsort(){
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];
if(--deg[y]==0) q.push(y);
}
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&n1,&n2);
add(n1,n2);
}
topsort();
for(int i=cnt;i>0;--i){
int x = a[i];
bs[x][x]=1;
for(int j=head[x];j;j=Next[j]){
int y=ver[j];
bs[x]=bs[x]|bs[y];
}
}
for(int i=1;i<=n;i++){
cout<<bs[i].count()<<endl;
}
return 0;
}