给定一张N个点M条边的有向无环图,分别统计从每个点出发能够到达的点的数量。
输入格式
第一行两个整数N,M,接下来M行每行两个整数x,y,表示从x到y的一条有向边。
输出格式
输出共N行,表示每个点能够到达的点的数量。
数据范围
1<=n,m<=30000
输入样例:
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
题解 :拓扑排序适用于有向无环图(DAG),我们用拓扑排序对所有点进行排序,我们可以得到一个有顺序的序列(不是大小顺序,是优先顺序 )。
这样我们倒着枚举,排完序的序列最后一个数肯定出度为0,也就是只能自己指向自己,向前遍历,前一个数是基于他能够到达的后一个数决定的。
问题来了,我们如何计算呢,题目中给的内存空间为256M,如果我们开一个30000 * 30000的二维数组,1e6大约4M ,这个二维数组3600M炸内存了。
我们可以利用bitset函数进行位运算,bitset<30000> f[30000] f【i】代表每一个点,第二维代表是一个01串(二进制),int型可以代表32个二进制数(0代表到不了,1代表可以到),所以空间大约为(30000 * 30000)/(32 ) 约等于 120M (√)
二进制位运算可以避免重复 0 1得1 , 1 1 得1
前面的方案数由后面的方案书累加起来。
最后bitset可以直接输出这个01串中的1的个数
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<bitset>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=30010;
vector<ll> vec[maxn];
bitset<maxn> f[maxn];
ll top[maxn]={0},indeg[maxn]={0};
ll n,m;
void topsort()
{
ll k=0;
queue<ll> q;
for(int i=1;i<=n;i++)
{
if(!indeg[i])
q.push(i);
}
while(q.size())
{
ll x=q.front();
q.pop();
top[++k]=x;
for(int i=0;i<vec[x].size();i++)
{
if(!--indeg[vec[x][i]])
q.push(vec[x][i]);
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
ll a,b;
cin>>a>>b;
vec[a].push_back(b);
indeg[b]++;
}
topsort();
for(int i=n;i>=1;i--)
{
ll k=top[i];
f[k][k]=1;
for(int i=0;i<vec[k].size();i++)
{
f[k]|=f[vec[k][i]];
}
}
for(int i=1;i<=n;i++)
{
cout<<f[i].count()<<endl;
}
return 0;
}