题目:
给定一张 N个点 M条边的有向无环图,分别统计从每个点出发能够到达的点的数量。
思路:
采用邻接表存图的方式,然后对这张图中节点进行拓扑排序,排序完成后能够得到一个拓扑序列,对于拓扑序列中任意一条边(x,y),x都在y之前出现。倒序遍历拓扑序列中的点,设集合f[i]为第i个点能到达的点的状态(压缩成二进制最大就是30000位二进制)
f[i]=集合{f[a],f[b],f[c],...f[z]}(a,b....z为i拓扑序列后面的节点)的并集
就相当于f[i]分别或(|)上f[a],f[b]....f[z]
这个操作可以利用bitset库,将N位二进制数压缩成N/32+1个unsigned int
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e4+10;
int n,m;//n个点,m条边
int e[N],ne[N],head[N],idx;//存储邻接表
int ind[N],top_arr[N];//入度,拓扑排序序列
bitset<N>f[N];
//二进制(1代表能够到达,0代表不能)记录当前点能够到达的点
void add(int x,int y){//邻接表加边
e[idx]=y;ne[idx]=head[x];head[x]=idx++;
}
void topsort(){//拓扑排序
queue<int> q;
for(int i=1;i<=n;i++){
if(ind[i]==0){
q.push(i);
}
}
int cnt=0;
while(q.size()){
int p=q.front();q.pop();
top_arr[++cnt]=p;
for(int i=head[p];i;i=ne[i]){
int j=e[i];
if(--ind[j]==0)q.push(j);
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
ind[y]++;//入度
add(x,y);//加边
}
topsort();
for(int i=n;i>=1;i--){//倒序遍历拓扑序列中的点
int now=top_arr[i];
f[now][now]=1;//对于当前节点now它本身肯定是能够到达自己的
for(int j=head[now];j;j=ne[j]){//当前点所连接的点
f[now]|=f[e[j]];//求并集
}
}
for(int i=1;i<=n;i++)printf("%d\n",f[i].count());
return 0;
}
总结: