题目链接:https://www.acwing.com/problem/content/166/
题目:
给定一张 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
分析:
由于此题是一个有向无环图,所以可以使用拓扑排序的方式进行求解。
将得到的拓扑序求出来后,我们可以从后往前求解。 将后面节点能够到达的节点统计下来。f[i]:表示的就是节点i能够到达的所有节点的集合的形式。而k 节点能够到达 i 节点,则k节点一定能够可以到达 i节点能够经过的节点。
举例:k能够到达i1,i2,i3节点,而i1,i2,i3节点能够到的点已经计算好了为f[i],
那么f[k] = f[i1] 并上 f[i2] 并上 f[i3]所能经过的所有节点 + k自己
而这个过程就可以使用位运算中的bitset<>进行优化,可以经过哪个点,则这个点对应的位置就置为1,不能经过哪个点就置为0.
而f[i1] ,f[i2],f[i3]....的并集就可以用 或运算的方式实现了。
最后统计 哪些位置为1,就是可以到达哪些位置。
bitset的使用:
bitset<maxn(长度)> f;
f.reset() //全部变成0
f.count() //统计1的个数
还可以使用可使用| , &等操作
代码实现:
# include <iostream>
# include <cstring>
# include <bitset>
# include <queue>
using namespace std;
const int N = 30010;
int h[N],e[N],ne[N],idx;
bitset<N> f[N]; // f[i]的含义是i这个点能够到哪些点,而使用bitset<>后,那么那个点所对应位置就被置为1。
int rudu[N]; // topsort找入度为0的情况
int n,m;
queue<int> q; // 拓扑排序一般用BFS的方式去实现
int topxu[N]; // 拓扑排序的结果
int cnt = 0;
void add(int a ,int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
void topsort()
{
for(int i = 1 ; i <= n ; i++)
{
if(rudu[i] == 0)
{
q.push(i);
}
}
while(q.size())
{
int temp = q.front();
q.pop();
topxu[++cnt] = temp;
for(int i = h[temp] ; i != -1; i = ne[i])
{
int j = e[i];
rudu[j]--;
if(rudu[j] == 0)
{
q.push(j);
}
}
}
}
int main()
{
memset(h,-1,sizeof h);
scanf("%d %d",&n,&m);
for(int i = 1 ; i <= m ; i++)
{
int a,b;
scanf("%d %d",&a,&b);
add(a,b);
rudu[b]++;
}
topsort();
for(int i = n ; i >= 1 ; i--)
{
int j = topxu[i];
f[j][j] = 1;
for(int k = h[j] ; k != -1 ; k = ne[k])
{
int t = e[k];
f[j] |= f[t];
}
}
for(int i = 1 ; i <= n ; i++)
{
printf("%ld\n",f[i].count());
}
return 0;
}