CSP-S 2022 T3 题解

简述题意

一张 \(n\) 个点,\(m\) 条边的有向图,有 \(q\) 次操作,每次删掉一条已有的边,恢复一条被删的边,删掉一个点的所有入边,回复一个点的所有入边。每次操作后询问此时图是不是一个内向基环树森林。

数据范围:\(1\le n,m,q\le 5\times10^5\)

解题思路
15pts

我会 spfa 判正环!(希望我是唯一一个考场上这样写的人。)复杂度为 \(O(n^2q)\)

40pts

直接 DFS 就可以判环了,按题意模拟即可。复杂度为 \(O(nq)\)

60pts

考虑基环树的性质:基环树有 \(n\) 个点,\(n\) 条边。

我们不难证明每个点出度为 \(1\) 的图就是内向基环树森林。

直接维护每个点的出度即可,可以过掉有特殊性质的点,但还是 \(O(nq)\) 的。

100pts

动态内向基环树森林的判断不弱于动态仙人掌,这种东西显然不是 CSP 该考的。发现我们只需要判断,于是可以想到 Hash。

我们给每个点一个 \([0,V)\cap\mathbb{Z}\) 内均匀随机的权值 \(a_i\),定义一个点的点权为 \(v_t=\sum\limits_{s\rightarrow t\in E}a_s\),如果操作完后有 \(\sum v_i=\sum a_i\),我们就认为该图是内向基环树森林。(所有运算均在 \(\bmod V\) 的意义下进行。)

设一个点的出度为 \(o_i\),记 \(S=\sum o_ia_i\)打表发现 \(S\) 在值域内大概是均匀分布的,于是 Hash 冲突的概率大约就是 \(O(\frac{1}{V})\)。利用 unsigned long long 自然溢出实测可过。

代码

给出主要实现:

const int N=5e5+5;
mt19937_64 rnd(chrono::steady_clock::now().time_since_epoch().count());
ull val[N],have[N],all[N],now,tmp;
int n,m,q;
inline void add(int x,ull k) { now+=k,have[x]+=k; }

signed main()
{
	read_(n,m);
	irep(i,1,n) val[i]=rnd(),tmp+=val[i];
	for(int i=1,u,v;i<=m;++i) read_(u,v),all[v]+=val[u],add(v,val[u]);
	read_(q);
	for(int i=1,op,u,v;i<=q;++i)
	{
		read_(op);
		if(op&1) read_(u,v);
		else read_(u);
		switch(op)
		{
		case 1: add(v,-val[u]); break;
		case 2: add(u,-have[u]); break;
		case 3: add(v,val[u]); break;
		case 4: add(u,all[u]-have[u]); break;
		} write_(now==tmp?"YES\n":"NO\n");
	}
	return 0;
}

个人认为这题思维难度比较大,毕竟 CSP/NOIP 没有怎么考过随机化技巧,比较考察平时对题型的积累。

posted @ 2022-10-31 17:44  嘉年华_efX  阅读(115)  评论(0编辑  收藏  举报