其他分享
首页 > 其他分享> > 「BZOJ3569」DZJ Loves Chinese II

「BZOJ3569」DZJ Loves Chinese II

作者:互联网

题目

点这里看题目。

分析

神奇的题目啊!

以下设被删除的边集为 \(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\) 出来,并定一个根。在这个基础上,有:


这道题就是给出了 \(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