ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

「BZOJ3569」DZJ Loves Chinese II

2022-07-13 12:33:37  阅读:183  来源: 互联网

标签:int subseteq DZJ omega II read 回路 mathcal BZOJ3569


题目

点这里看题目。

分析

神奇的题目啊!

以下设被删除的边集为 \(Q\)。

思路一

正常人的思路。

随便拉一棵生成树 \(T\),并定一个根。假如我们只删除了一条树边 \(e\),设 \(S(e)\) 为覆盖 \(e\) 的非树边的集合,则图不连通当且仅当 \(Q\supseteq S(e)\)。

那么删除了多条树边呢?假如我们钦定了一个树边集合 \(Q_T\subseteq Q\),\(Q_T\) 中的边对应的子树两两不相交,则我们将上一步的方法拓展,我们只需要知道是否有 \(Q\supseteq(\Delta_{e\in Q_T}S(e))\),其中 \(\Delta\) 表示集合的对称差。由于子树两两不交,因此跨子树的边会被差掉。

合理地猜测一下,我们可以猜想在 \(Q_T\) 中边的子树存在包含关系时,类似的想法仍然成立——假如令非树边的 \(S(e)=\{e\}\),则我们只需要检查是否存在 \(Q'\subseteq Q,Q'\neq \varnothing\), 使得 \(\Delta_{e\in Q'}S(e)=\varnothing\)。(旧的想法需要限制 \(Q'\) 中树边的子树互不相交)

至少我并没能很快地建立一个证明(似乎只需要证明,不符合第二步条件的 \(Q'\) 不会影响结果即可)。不过我们验证的方法很多,比如疯狂对拍。没有试一下这个推广过后的想法对不对实在是可惜。


具体实现起来,可以做一个简单的 hash,用随机权值的异或替代集合的异或。给一个非树边的权值 \(w:E\subseteq T\rightarrow \{0,1\}^\omega\),则我们可以定义 \(f(e)=\bigoplus_{x\in S(e)}w(x)\)。询问只需要检查 \(Q\) 中的是否有异或为 \(0\) 的权值子集即可。

复杂度可以做到 \(O(n+\omega qk)\)。

Remark.

随机赋权这个思路......其实挺常见的?

hash 检查的目的就是要简化运算,代价是降低了正确概率。“赋权”是一种 hash,而“随机”就是保证正确率。此外,在赋权之后还要建立运算的映射关系。

做一些判断的时候,不要忘记了这种方法吧。

思路二

学过线性代数的人的思路。我没学过,抄的

给出一个简单无向连通图 \(G=(V,E)\),定义 \(\mathbb{GF}(2)\) 上的若干个线性空间:

  1. 边空间:幂集 \(2^E\) 对应的线性空间,干脆叫 \(\mathcal E\)。

  2. 回路空间:图上的回路 \(C\) 是一个使得每个点在 \(C\) 中邻接边的条数均为偶数的 \(E\) 的子集。因为两个回路做对称差之后仍然是回路,所以回路也可以对应到一个线性空间 \(\mathcal C\) 且为 \(\mathcal E\) 的子空间。

  3. 割空间:若 \(D\subseteq E\) 是一个割,当且仅当存在 \(S\subseteq V\),使得 \(D=\{(u,v)|u\in S,v\in V\setminus S\}\)。由于 \(D\) 实质上就是 \(S\) 中点的邻接边的对称差,因此两个割对称差之后还是割,所以割也可以对应到线性空间 \(\mathcal D\) 且为 \(\mathcal E\) 的子空间。

随便从 \(G\) 上面拉一棵生成树 \(T\) 出来,并定一个根。在这个基础上,有:

  • \(\mathcal C\) 的一组基由 \(T\) 上的所有“简单环”构成。

    简单环就是指一条非树边 \(e=(u,v)\) 和 \(T\) 上 \(u\) 到 \(v\) 的路径上的边共同构成的一个回路。

    这个东西比较浅显易懂。经典的 「WC2011」最大 XOR 和路径 可以算是用到了这个东西。

  • \(\mathcal D\) 的一组基由 \(T\) 上所有子树(不包括 \(T\) 本身)对应的点集对应的割构成。

    Explanation.

    首先,容易发现两个割不同等价于生成它们的点集不同(虽然有两种点集,不过大概是这个意思)。这样就容易发现由子树生成的割彼此“线性无关”。

    另一方面,我们不难构造一种方案,来用 \(T\) 上所有子树构造处某一个割的一部点集。(从最浅的元素开始构造即可)

  • 对于 \(E_1,E_2\subseteq E\),可以发现对应的向量的内积为 \(|E_1\cap E_2|\mod 2\)。

    那么,可以证明,对于任意一个回路 \(C\) 和割 \(D\),二者对应的向量必然正交

    Explanation.

    对于任何一个 \(C\),考虑其中的所有环 \(\mathring C\)(环既是回路,也是路径)。一个环 \(\mathring C\) 必然和 \(D\) 交出偶数条边。环在回路中彼此不交,因而回路也会和 \(D\) 交出偶数条边。

  • 在线性空间 \(\mathcal E\) 中,\(\mathcal C\) 是 \(\mathcal D\) 互为正交补。

    正交补就是“与某个子空间的向量均正交的空间内向量构成的空间“。

    Explanation.

    根据对基的考察,我们有 \(\dim \mathcal C=|E|-|V|+1,\dim \mathcal D=|V|-1\)。


这道题就是给出了 \(Q\),询问 \(Q\) 对应的向量是否 \(\in \mathcal D\)。

事实上根据对于 \(\mathcal D\) 的基的讨论就已经足够解决这个问题了。拉一棵生成树 \(T\) 并定根,我们对于每条树边 \(e\),算出覆盖它的边的集合 \(S'(e)\)(包括它自己)。则我们只需要检查是否存在一个 \(P\subseteq T,Q'\subseteq Q\),使得 \(\Delta_{e\in P}S'(e)=Q'\) 即可。

事实上,我们已经可以从这里生成“思路一”的解法了。稍加修改即可。

顺便补充一下正确概率:

Note.

我们相当于要算 \(S\neq \varnothing\) 而 \(f(S)=0\) 的概率。

呃......似乎很容易。从 \(S\) 中去掉一个元素 \(x\),则这就是要求 \(w(x)=w(S\setminus\{x\})\)。这样可以粗略估得概率为 \(2^{-\omega}\)。

所以,如果要求出错概率为 \(\varepsilon_p\),则 \(\omega=\log_2\varepsilon_p^{-1}\)。若计算机位宽为 \(\omega_0\),则复杂度大概就是 \(O(\frac{\omega}{\omega_0}(n+qk))\)。

代码

#include <cmath>
#include <cstdio>
#include <random>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

typedef unsigned long long ull;

const int MAXN = 1e5 + 5, MAXM = 5e5 + 5, MAXLOG = 18;

template<typename _T>
void read( _T &x ) {
	x = 0; char s = getchar(); int f = 1;
	while( ! ( '0' <= s && s <= '9' ) ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
	x *= f;
}

template<typename _T>
void write( _T x ) {
	if( x < 0 ) putchar( '-' ), x = -x;
	if( 9 < x ) write( x / 10 );
	putchar( x % 10 + '0' );
}

struct Edge {
	int to, nxt;
} Graph[MAXM << 1];

ull bas[64];
int seq[20], tot;

int head[MAXN], cnt = 1;
ull tag[MAXN];

int fr[MAXM], to[MAXM];
bool onTre[MAXM];
ull wei[MAXM];

bool vis[MAXN];

int N, M, Q, lg2;

inline void AddEdge( const int &from, const int &to ) {
	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
	head[from] = cnt;
}

void DFS( const int &u ) {
	vis[u] = true;
	for( int i = head[u], v ; i ; i = Graph[i].nxt )
		if( ! vis[v = Graph[i].to] ) 
			onTre[i >> 1] = true, DFS( v ); 
}

void Recover( const int &u, const int &fa ) {
	for( int i = head[u], v ; i ; i = Graph[i].nxt )
		if( onTre[i >> 1] && ( v = Graph[i].to ) ^ fa ) {
			Recover( v, u );
			tag[u] ^= tag[v];
			wei[i >> 1] = tag[v];
		}
}

inline bool Insert( ull x ) {
	per( i, 63, 0 ) {
		if( ! ( x >> i & 1 ) ) continue;
		if( ! bas[i] ) { bas[i] = x; return true; }
		x ^= bas[i];
	}
	return false;
}

int main() {
	static std :: mt19937_64 rng( 998244853 );
	static std :: uniform_int_distribution<ull> genHash( 0, ( ull ) -1 );

	read( N ), read( M );
	rep( i, 1, M ) {
		read( fr[i] ), read( to[i] );
		AddEdge( fr[i], to[i] );
		AddEdge( to[i], fr[i] );
	}
	DFS( 1 );
	rep( i, 1, M ) if( ! onTre[i] ) {
		wei[i] = genHash( rng );
		tag[fr[i]] ^= wei[i];
		tag[to[i]] ^= wei[i];
	}
	Recover( 1, 0 );
	int ansCount = 0;
	for( read( Q ) ; Q -- ; ) {
		read( tot );
		rep( i, 1, tot ) read( seq[i] ), seq[i] ^= ansCount;
		bool flg = true;
		rep( i, 0, 63 ) bas[i] = 0;
		rep( i, 1, tot ) flg &= Insert( wei[seq[i]] );
		if( flg ) puts( "Connected" ), ansCount ++;
		else puts( "Disconnected" );
	}
	return 0;
}

标签:int,subseteq,DZJ,omega,II,read,回路,mathcal,BZOJ3569
来源: https://www.cnblogs.com/crashed/p/16473418.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有