P10480 可达性统计(拓扑,bitset 优化)

link

从数的角度来看,如果知道任意一个点能到达的点的数量,那么它的前驱节点一定也能到达,但是,只累加数的话无法处理可能存在重合点的情况。

所以,考虑从集合的角度,设 f(x) 表示 x 能到达的点的集合

如果 x 有邻点 y1,y2,...,yk,那么 x 能到达的点就是它的邻点能到达的点的并集

f(x)={x}f(y1)f(y2)...f(yk)

在 DAG 里边,我们可以先跑个拓扑序出来,从后往前处理 f(x),这样没有后效性

时间复杂度呢 ... 我觉得可以从每个点的贡献角度想,

访问集合中的每个点时间贡献为 O(1),考虑极限情况:1 可以到达 2 ~ n,2 可以达到 3 ~ n ... 那么每访问完一个 f(x),贡献分别为 n - 1,n - 2 ... 2,1

累加起来就是 O(n(n1)2),比拓扑排序高,瓶颈,且不能接受


考虑位运算优化,也就是状态压缩的思想

这里有个很冷门?的工具 bitset<N>,表示一个 N 位的二进制数,每八位占用一个字节

而我们知道一个 int 变量是有 2321 的最大值,也就是说一个 int 可以存 w=32 位二进制数

那么 N 位的 bitset 执行一次位运算的复杂度就为 O(Nw)

所以所以,每个集合 f(x) 假设有 n 个数,状态压缩完就是一个 n 位的二进制数,再用 bitset 压缩,每 32 位压缩为 1 位,那么总的复杂度就降到 O(n(n1)2w),大概 1.5e7 的样子

#include <bits/stdc++.h>
#define re register int
using namespace std;
const int N = 3e4 + 10;
struct Edge
{
int to, next;
}e[N];
int top, h[N], in[N];
int n, m;
vector<int> seq;
bitset<N> f[N];
inline void add(int x, int y)
{
e[++ top] = (Edge){y, h[x]};
h[x] = top;
}
inline void topsort()
{
queue<int> q;
for (re i = 1; i <= n; i ++)
if (in[i] == 0) q.push(i);
while (!q.empty())
{
int x = q.front(); q.pop();
seq.push_back(x);
for (re i = h[x]; i; i = e[i].next)
{
int y = e[i].to;
if (-- in[y] == 0)
q.push(y);
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m;
while (m --)
{
int x, y; cin >> x >> y;
add(x, y);
in[y] ++;
}
topsort();
for (re i = seq.size() - 1; i >= 0; i --)
{
int u = seq[i];
f[u][u] = 1;
for (re j = h[u]; j; j = e[j].next)
{
int v = e[j].to;
f[u] |= f[v];
}
}
for (re i = 1; i <= n; i ++)
cout << f[i].count() << '\n'; //bitset成员函数,返回有多少位是 1
return 0;
}
posted @   Zhang_Wenjie  阅读(25)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示