本题思路:
要求从一个点出发有多少个点可以从该点出发后到达 那么想到topsort后对于任意一条边(x,y)x都排在y前面
我们用一个N位的二进制数来存储每一个f[x] 其中第i位为1则代表该点可以到达i而0则表示该点不能到达
对若干个集合求并集后就是对n个二进制数做按位异或运算 最后f[x]中1的个数就是从x出发能够到达的节点数量
Topsort的实现方式
1.先建立一个拓扑序列seq
2.预处理出所有点的入度d[i],并将所有入度为0的点入队
3.取出队头结点t,将t加到队列的尾端
4.对于从t出发的每条边(t,j) 把d[j]– 若被减为0 则将j入队
5.重复第3~4步的过程直到队列为空 seq则为所求
这题和之前洛谷那道P3916 图的遍历看起来相似却又不同 还是很有意思的
#include <iostream>
#include <cstring>
#include <algorithm>
#include<bitset>
#include<queue>
using namespace std;
const int N = 30010;
int h[N], e[N], ne[N], idx;
int d[N],seq[N];
int n,m;
bitset<N> f[N];//N位的一个二进制数字 状态压缩
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void topsort()
{
queue<int> q;
for (int i = 1; i <= n; i ++ )
if (!d[i])
q.push(i);
int k = 0;
while (q.size())
{
int t = q.front();
q.pop();
seq[k ++ ] = t;
for (int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
if (-- d[j] == 0)
q.push(j);
}
}
}
int main()
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);
while(m--)
{
int a, b;
cin>>a>>b;
add(a, b);
d[b]++;
}
topsort();
for(int i=n-1;i>=0;i--)
{
int j=seq[i];
f[j][j]=1;
for(int k=h[j];~k;k=ne[k])
{
f[j]|=f[e[k]];
}
}
for(int i=1;i<=n;i++) cout<<f[i].count()<<endl;
return 0;
}