Loading

【题解】图 from 模拟赛 2024.2.14

B. 图

一张无向图,\(n\) 个点,\(m\) 条边,选择一个非空点集和两端都在集合内的边,求选出来的是一个连通块的方案数,模 \(2\)

\(1\le n\le 50,\space |u-v|\le 12\)


连通块很难维护,最小表示法会炸。

一个小 trick:在难以处理的条件中,把题目要求的东西转化成另一个东西,条件完全不一样,但是求出后可以容易推出原来的东西。

比如这里,选择一个点集,然后对每个点黑白染色,同一个连通块的点颜色相同,不难发现染色方案数就是 \(2^{\text{连通块个数}}\)

我们要的是只有一个连通块的方案数,如果个数 \(>2\),我们把对方案数 \(\bmod 4\) 即可去掉。

\(f[i,j]\) 表示前 \(i\) 个点,点 \(i-11\sim i\) 的染色状态为 \(j\),其中 \(0\) 表示不选这个点,\(1\) 表示染白,\(2\) 表示染黑的方案数,用三进制表示。

转移就变得很容易,最后答案对 \(4\) 去摸,注意存在所有点不选的方案数 \(1\) 种。

点击查看代码
#include<bits/stdc++.h>
#define ls(p) a[p].lc
#define rs(p) a[p].rc
#define fi first
#define se second
#define mkp make_pair
#define ll int
#define pir pair<ll,ll>
#define pb push_back
using namespace std;
const ll maxn=52, inf=1e9, mod=1e9+7, M=531441;
ll n,m,u,v,msk[maxn],f[maxn][M],c1[M],c2[M],ans;
int main(){ 
	scanf("%d%d",&n,&m);
	for(ll i=1;i<=m;i++){
		scanf("%d%d",&u,&v);
		if(u>v) swap(u,v);
		msk[v]|=(1<<v-u-1);
	}
	for(ll i=0;i<M;i++){
		for(ll j=1,x=i;j<=12;j++){
			if(x%3==1) c1[i]|=(1<<j-1);
			else if(x%3==2) c2[i]|=(1<<j-1);
			x/=3;
		}
	}
	f[0][0]=1;
	for(ll i=1;i<=n;i++){
		for(ll j=0;j<M;j++){
			if(i==1&&j==2) break;
			if(!f[i-1][j]) continue;
			ll w=j*3%M;
			(f[i][w]+=f[i-1][j])%=4;
			if(!(c2[j]&msk[i])) (f[i][w+1]+=f[i-1][j])%=4;
			if(!(c1[j]&msk[i])) (f[i][w+2]+=f[i-1][j])%=4;
		}
	}
	for(ll i=0;i<M;i++) (ans+=f[n][i])%=4;
	printf("%lld",ans>>1);
	return 0;
}
posted @ 2024-02-14 15:17  Lgx_Q  阅读(8)  评论(0编辑  收藏  举报