第十八届吉林省大学生程序设计竞赛 I 题题解

Black and White Coloring

Black and White Coloring

神奇不等式

从一个神奇不等式开始,假设先手的涂色确定了 \(m_1\) 条边得分,后手随机等概率地给剩余点涂色,那么我们有得分期望如下

\[E(X)=m_1+\frac{m-m_1}2=\frac{m+m_1}{2}\ge \frac m 2 \]

我们可以从这个不等式得到三个推论。

引理 1

不论先手如何染色,后手总有方法使得得分 \(X\ge\dfrac m 2\)

由期望定义 \(E(X)=\sum iP(x=i)\) 可得,若所有方法得分都小于 $\dfrac m 2 $ 那么得分期望一定小于 $\dfrac m 2 $ ,出现矛盾。

引理 2

要让后手染色后得分不超过 $\dfrac m 2 $ ,那么就要让后手无论如何染色得分都等于 $\dfrac m 2 $ 。

首先由引理 1 得到后手一定存在一种方法使得分 $X\ge\dfrac m 2 $ ,若还存在方法使得分 $X\lt\dfrac m 2 $ ,那么期望得分一定小于 $\dfrac m 2 $,出现矛盾。

引理 3

图上相邻节点一定不会都在先手被染色。

若图上相邻节点在先手被染色,那么 \(m_1>0\) ,$E(x)>\dfrac m 2 $ ,后手至少存在一种方法使得分超过 $\dfrac m 2 $ 。

引理 4

图上相邻节点一定不会都在后手被染色。

若图上相邻节点都在后手被染色,那么对于这两个节点的四种染色方式一定会出现其中两种得分不同,与引理 2 矛盾。

由引理 3 和引理 4 可知,图上相邻的节点会被分成先手染色 \(S\) 和后手染色 \(T\) 两个部分。

引理 5

\(\text{deg}_x\) 为点 \(x\) 的度数,所有 \(\text{deg}\) 为奇数的点都应该在先手被染色。

若存在点 \(x\) 在后手被染色, \(\text{deg}_x\) 为奇数。假设除 \(x\) 以外其余点被染色完成,此时 \(x\) 涂上不同颜色后得分不同,与引理 2 矛盾。

引理 6

\(x\in T\)\(\text{deg}_x=2\) ,那么与 \(x\) 相连的两点 \(y,z\) 应该在先手被染上不同颜色。

\(y,z\) 在先手被染上相同颜色,那么 \(x\) 涂上不同颜色后得分不同,与引理 2 矛盾。

解法

至此,我们已经可以得到这道题的解法了。遍历原图的每个连通块,判断每个连通块是否都是二分图,存在不是二分图的连通块则无解。在联通块内部分出两部 \(A,B\) ,计算 \(\text{sum}=\text{calc}(A,B)+\text{calc}(B,A)\) 。在 \(\text{clac}(S,T)\) 这个函数中将 \(S\) 作为先手染色点集, \(T\) 作为后手染色点集。根据引理 6 ,对于 \(x\in T\)\(\text{deg}_x=2\) ,将与点 \(x\) 相连的两点 \(y,z\) 相连得到边 \((y,z)\) 并加入边集 \(E\) ,得到新图 \(G(S,E)\) 。判断新图 \(G\) 是否是二分图,并统计联通块数量 \(k\) ,若是则返回 \(2^k\) ,若不是则返回 0 。最终的答案就是所有联通块的 \(\text{sum}\) 相乘。

#include<bits/stdc++.h>
using namespace std;
const int P=1e9+7;
long long qpow(long long a,long long k)
{
	long long mulv=1;
	while(k)
	{
		if(k&1)mulv=mulv*a%P;
		a=a*a%P;
		k>>=1;
	}
	return mulv;
}
void solve()
{
	long long n,m,ans=1;
	cin>>n>>m;
	deque<bool> vis1(n+1),vis2(n+1),col1(n+1),col2(n+1);
	vector<vector<int>> adj1(n+1),adj2(n+1);
	for(int i=1;i<=m;i++)
	{
		int u,v;
		cin>>u>>v;
		adj1[u].push_back(v);
		adj1[v].push_back(u);
	}
	auto calc=[&](vector<int> &S,vector<int> &T)
	{
		for(auto ti:T)
		{
			if(adj1[ti].size()&1)return 0ll;
			else if(adj1[ti].size()==2)
			{
				int u=adj1[ti][0];
				int v=adj1[ti][1];
				adj2[u].push_back(v);
				adj2[v].push_back(u);
			}
		}
		bool flag=true;
		auto dfs=[&](auto &&dfs,int o)->void
		{
			vis2[o]=true;
			for(auto v:adj2[o])
			{
				if(vis2[v])
				{
					flag&=(col2[o]^col2[v]);
					continue;
				}
				col2[v]=col2[o]^1;
				dfs(dfs,v);
			}
		};
		int cnt=0;
		for(int si:S)
		{
			if(vis2[si])continue;
			col2[si]=0;
			dfs(dfs,si);
			if(!flag)return 0ll;
			cnt++;
		}
		return qpow(2,cnt);
	};
	for(int i=1;i<=n;i++)
	{
		if(vis1[i])continue;
		bool flag=true;
		vector<int> S,T;
		auto dfs=[&](auto &&dfs,int o)->void
		{
			vis1[o]=true;
			if(col1[o])S.push_back(o);
			else T.push_back(o);
			for(auto v:adj1[o])
			{
				if(vis1[v])
				{
					flag&=(col1[o]^col1[v]);
					continue;
				}
				col1[v]=col1[o]^1;
				dfs(dfs,v);
			}
		};
		dfs(dfs,i);
		if(!flag)
		{
			cout<<0<<"\n";
			return ;
		}
		long long sum=calc(S,T)+calc(T,S);
		ans=ans*sum%P;
	}
	cout<<ans<<"\n";
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int T;
	cin>>T;
	for(int i=1;i<=T;i++)solve();
	return 0;
}

posted @ 2025-05-28 17:13  wallcrack  阅读(62)  评论(0)    收藏  举报