描述
给定一张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
拓扑排序+位运算
用位运算代表经过了某个点,按照拓扑排序倒序进行计算,一个点的点集数是自身并上所有出边的点的点集
#include <queue>
#include <bitset>
#include <iostream>
using namespace std;
static const auto io_sync_off = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
return nullptr;
}();
const int maxn = 30005;
int n, m, tot, cnt, a[maxn];
int head[maxn], Next[maxn], ver[maxn], deg[maxn];
bitset<maxn> f[maxn];
void add(int x, int y)
{
Next[++tot] = head[x], head[x] = tot;
ver[tot] = y, ++deg[y];
}
void topsort()
{
queue<int> q;
for (int i = 1; i <= n; ++i)
if (deg[i] == 0) q.push(i);
while (!q.empty())
{
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);
}
}
}
void calc()
{
for (int i = cnt; i; --i)//按照倒叙,这样用到的点集会在先前求出
{
int x = a[i];
f[x][x] = 1; //经过了自己
for (int i = head[x]; i; i = Next[i])
{
int y = ver[i];//所有出边
f[x] |= f[y]; //按位或
}
}
}
int main()
{
cin >> n >> m;
for (int i = 0; i < m; ++i)
{
int x, y;
cin >> x >> y;
add(x, y);
}
topsort();
calc();
for (int i = 1; i <= n; ++i)
cout << f[i].count() << endl;//1的数量也就是能经过的点的个数
return 0;
}