拓扑排序 DP 求一个点能到的点个数 T3 164. 可达性统计 - AcWing题库 给定一张 个点 条边的有向无环图,分别统计从每个点出发能够到达的点的数量。
输入格式
第一行两个整数 ,接下来 行每行两个整数 ,表示从 到 的一条有向边。
输出格式
输出共 行,表示每个点能够到达的点的数量。
数据范围
,
输入样例:
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
思路
求从每个点出发能够到达的点的数量, 朴素解法可以用DFS求每个点能走到的数量, 但时间复杂度最坏为 。
考虑一下DP
状态表示:f[i]
i点能到的点的集合, 初始只有i自己
状态计算:f[i] = f[i] | f[j]
, j 为 i 的子节点, 取并集即可, 最后求一下集合中1的个数
若采用bool数组实现, 有n个点m条边, 时间复杂度最坏为 , 这里就用二进制来实现, 除了手写二进制, 也可以用STL库中的 bitset。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <bitset>
using namespace std;
const int N = 3e4 + 10, M =2 * N;
int n,m;
int dist[N];
int q[N];
int din[N];
int h[N], ne[M], e[M], idx;
bitset<N> f[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] =h[a], h[a] = idx++;
}
void topsort()
{
int hh= 0, tt = -1;
for(int i = 1; i <= n; i++)
if(!din[i]) q[++tt] = i;
while(hh <= tt)
{
int t = q[hh++];
for(int i = h[t]; ~i ; i = ne[i])
{
int j = e[i];
if(--din[j] == 0) q[++tt] =j;
}
}
}
int main()
{
memset(h, -1, sizeof h);
cin >> n >> m;
while(m--)
{
int a, b;
cin >> a >> b;
add(a,b);
din[b]++;
}
topsort();
for(int i = n - 1; i >= 0; i--)
{
int j = q[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;
}