首页 > 其他分享 >P10480 可达性统计(拓扑,bitset 优化)

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

时间:2024-07-23 13:17:56浏览次数:6  
标签:... int 拓扑 P10480 到达 re bitset 可达性

link

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

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

如果 \(x\) 有邻点 \(y_1,y_2,...,y_k\),那么 \(x\) 能到达的点就是它的邻点能到达的点的并集

\[f(x) = \{x\} \cup f(y_1) \cup f(y_2) ... \cup f(y_k) \]

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

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

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

累加起来就是 \(O(\frac{n(n-1)}{2})\),比拓扑排序高,瓶颈,且不能接受


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

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

而我们知道一个 int 变量是有 \(2^{32} - 1\) 的最大值,也就是说一个 int 可以存 \(w = 32\) 位二进制数

那么 N 位的 bitset 执行一次位运算的复杂度就为 \(O(\frac{N}{w})\)

所以所以,每个集合 \(f(x)\) 假设有 n 个数,状态压缩完就是一个 n 位的二进制数,再用 bitset 压缩,每 32 位压缩为 1 位,那么总的复杂度就降到 \(O(\frac{n(n-1)}{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;
}

标签:...,int,拓扑,P10480,到达,re,bitset,可达性
From: https://www.cnblogs.com/wenzieee/p/18318152

相关文章

  • [namespace hdk] 64位 bitset
    功能已重载运算符[](int)~()+(bitset)+(unsignedlonglong)+=(bitset)+=(unsignedlonglong)>==<>=<=(bitset\unsignedlonglong)<<>>max()min()已定义函数intsize()返回bitset大小intarray_size()返回bitset占用的unsigned_longlong......
  • 拓扑排序——AcWing 164. 可达性统计
    目录拓扑排序定义运用情况注意事项解题思路AcWing164.可达性统计题目描述运行代码代码思路改进思路拓扑排序定义拓扑排序(TopologicalSort)是对有向无环图(DirectedAcyclicGraph,简称DAG)的一种排序方式。在一个有向无环图中,拓扑排序的结果是一个线性的顶点序列,其......
  • 算法训练营第六十七天 | 卡码网110 字符串接龙、卡码网105 有向图的完全可达性、卡码
    卡码网110字符串接龙这题一开始用的邻接表+dfs,不幸超时#include<iostream>#include<list>#include<string>#include<vector>usingnamespacestd;intminLen=501;boolcount(stringa,stringb){intnum=0;for(inti=0;i<a.lengt......
  • bitset详解以及用法
    butset详解以及用法bitset是C++标准库中的一个类,它提供了一种方便的方式来操作位序列,常用于位运算和状态压缩。下面我将为您详细介绍bitset的基本概念、基本用法以及一些常用的成员函数。基本概念1、bitset可以看作是一个多位二进制数,其每一位都是0或1。2、它是......
  • JVM之GC篇:(一)引用计数与可达性分析
    文章目录0x00前言0x01引用计数0x02可达性分析0x03总结0x00前言GC的第一步就是要判断出哪些对象需要被回收。显然易见的是,当一个对象不再被使用后,那么就需要对其进行回收。那么问题就是,如何判断对象是否被使用?本文将介绍两种算法来判断对象的使用情况。0x01引......
  • bitset专题
    bitsetbitset前身:普通状态压缩的优化以cf937G为例,对于邻接矩阵的由二维压缩到一维#include<bits/stdc++.h>usingi64=longlong;voidsolve(){intn;std::cin>>n;std::vector<std::string>g(n),w(n);for(inti=0;i<n;i++){......
  • C117 莫队配合 bitset P4688 [Ynoi2016] 掉进兔子洞
    视频链接:C117莫队配合bitsetP4688[Ynoi2016]掉进兔子洞_哔哩哔哩_bilibili   LuoguP4688[Ynoi2016]掉进兔子洞//莫队配合bitsetO(n*sqrt(n))#include<iostream>#include<cstring>#include<algorithm>#include<cmath>#include<bitset>usin......
  • C++bitset类型
    bitset类型我们介绍了将整型运算对象当作二进制位集合处理的一些内置运算符。标准库还定义了bitset类,使得位运算的使用更为容易,并且能够处理超过最长整型类型大小的位集合。bitset类定义在头文件bitset中。定义和初始化bitsetbitset类是一个类模板,它类似array类,具有固定的......
  • [算法]分割等和子集算法 & bitset容器应用
    LeetCode416.分割等和子集1题目描述给你一个只包含正整数的非空数组nums。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。1.1输入测试示例1:输入:nums=[1,5,11,5]输出:true解释:数组可以分割成[1,5,5]和[11]。示例2:输入:nums=[1,2,3,5]......
  • 从CF1941D与1741E初探可达性DP
    Problem-D-Codeforces用记忆化搜索过的,然而DP能快300ms记忆化搜索|\(\texttt{set}\)模拟核心思路一致,都是通过定义一个状态,即在第t次到达第now点来去重剪枝记忆化搜索intn,m,x;std::vector<std::pair<int,char>>step;std::set<int>S;intgetClock(intx,......