题解 【AcWing 164. 可达性统计】
\(\large\mathcal{Description}\)
给定一张 \(N\) 个点 \(M\) 条边的有向无环图,分别统计从每个点出发能够到达的点的数量。
\(1\le N,M\le 30000\).
\(\large\mathcal{Solution}\)
设从点 \(x\) 出发能到达的所有点的集合为 \(f(x)\), \(x\) 连接的所有边为 \(y_1,y_2,\cdots y_k\). 则有:
\[f(x) = \{x\} \cup (\bigcup\limits_{1\le i\le k}f(y_i))
\]
即:从点 \(x\) 出发能到达的所有点就是从它连接的所有点出发能到达的所有点加上 \(x\) 本身。
考虑拓扑排序。在一张图的拓扑序中,对每条边 \((x, y)\) 总有:\(x\) 在 \(y\) 之前。而在上述的公式中我们要先算出 \(f(y)\) 在算出 \(f(x)\)。所以我们要按照拓扑序的倒序进行计算。
我们考虑将每个点 \(f(x)\) 记作一个 \(N\) 位二进制数。其中,若第 \(i\) 为 \(1\),则表示可以到达 \(i\);否则不能。那么就有 \(f(x)\) 中 \(1\) 的数量就是 \(x\) 发能够到达的点的数量。
考虑到如果拿二维数组来存所有点的 \(f(x)\) 的话,空间复杂度将达到 \(\Theta(n^2)\), 无法承受。从而想到可以把 \(N\) 位二进制数压缩到 bitset 中,这样空间复杂度将减小到原来的 \(\dfrac1{32}\),可以承受。
\(\large\mathcal{Code}\)
#include <bits/stdc++.h>
using namespace std;
const int N = 30010;
int n, m;
int Ecnt, head[N], nxt[N], ver[N], d[N];
int cnt, seq[N];
bitset<N> f[N];
void add(int x, int y)
{
ver[ ++ Ecnt] = y;
nxt[Ecnt] = head[x];
head[x] = Ecnt;
}
// 拓扑排序
void topsort()
{
queue<int> q;
for (int i = 1; i <= n; i ++ )
if (!d[i])
q.push(i);
while (!q.empty())
{
int x = q.front(); q.pop();
seq[ ++ cnt] = x;
for (int i = head[x]; i; i = nxt[i])
{
int y = ver[i];
if ( -- d[y] == 0) q.push(y);
}
}
}
int main()
{
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; i ++ )
{
int x, y;
scanf("%d %d", &x, &y);
add(x, y);
d[y] ++ ;
}
topsort();
for (int i = n; i >= 1; i -- ) // 注意要逆序遍历
{
int x = seq[i];
f[x][x] = 1;
for (int j = head[x]; j; j = nxt[j])
f[x] |= f[ver[j]];
}
for (int i = 1; i <= n; i ++ ) printf("%d\n", f[i].count());
return 0;
}

浙公网安备 33010602011771号