Loading

CF1221G 题解

特判 m=0。然后考虑做一个容斥。令 F(E)(E{0,1,2}) 表示边权集合 E(没有不在 E 内的数字)的方案数。那么答案为

F({0,1,2})F({0,1})F({1,2})F({0,2})+F({0})+F({1})+F({2})

对原图求孤点(所在连通块大小为 1)个数 c 与连通块个数 k(另一种做法是把孤点全部删掉再处理),显然有:

  • F({0,1,2})=2n
  • F({0})=F({2})=2c。这等价于,对于大小 >1 的所有连通块包含的点,它们的点权必须全为 0,或者全为 1
  • F({0,2})=2k。每个连通块要么全为 0,要么全为 1

以及:

  • 若原图不是二分图,那么 F({1})=0。对于每条边,它两端点的点权必须恰为 10。这就是黑白染色,也就是二分图的模型。
  • 否则,F({1})=2k。每个连通块有两种染色方案。

现在还剩下 F({0,1})F({1,2})。它们意即,原图中没有相邻的 1,或是没有相邻的 0。那么有 F({0,1})=F({1,2})

问题等价于,在原图中选出一个独立集,求方案数。

n=40,且考场有 n=20 的包,启发我们折半搜索。取阈值 B,对编号 B 的结点,枚举选择它们的所有合法方案 S,记 S 中所有点的邻接点集为 A,那么编号 >B 的结点,可选择的范围就是 T=[1,n]A。即,编号 >B 结点选择的合法方案,一定是 T 的子集。

这是一个明显的 SOS(Sum Over Subsets)计数。使用 FWT 等技巧可以在 O(2nB) 内完成。可我只会 Θ(3nB) 对每种可能的 T(B,n] 暴力枚举子集啊。分析复杂度:

Θ(2B+3nB)

2B=3nB,解得 B=n×log63。那么总复杂度就是 Θ(2B)n=40B24.525,取 B=24。完全不卡。

精细实现(预处理 A 等)可以让搜索过程的复杂度完全不带 nm

#include <cstdio>
#include <cmath>
#define ll long long

double log2(double x){
	return log(x)/log(2);
}

const int N=114514;

int F,S,A,B,adj[50],sad[50];

ll sos[N],ans;

void whl(int now){
	if(now>B) return ans+=sos[F^S],void();
	if(!(A&adj[now])){
		int ps=S,pa=A;S|=sad[now];A|=(1<<now-1);
		whl(now+1);S=ps;A=pa;
	}
	whl(now+1);
}

int n;bool bass[N];

void pre(int now){
	if(now>n-B) return bass[A]=1,void();
	if(!(A&adj[now])) A|=(1<<now-1),pre(now+1),A^=(1<<now-1);
	pre(now+1);
}

void sum(int now){
	if(now>n-B) return sos[S]+=bass[A],void();
	if(S&(1<<now-1)) A|=(1<<now-1),sum(now+1),A^=(1<<now-1);
	sum(now+1);
}

int G[50][50];

ll NoAdj(){
	B=n*log2(3)/log2(6);
	for(int i=0;i<n-B;++i) F|=(1<<i);
	for(int i=1;i<=n-B;++i) for(int j=1;j<=n-B;++j) adj[i]|=(G[i+B][j+B]<<(j-1));
	pre(1);for(S=0;S<(1<<n-B);++S) sum(1);
	for(int i=1;i<=B;++i){
		adj[i]=0;for(int j=1;j<=B;++j) adj[i]|=(G[i][j]<<(j-1));
	}
	for(int i=1;i<=B;++i) for(int j=B+1;j<=n;++j) sad[i]|=(G[i][j]<<(j-B-1));
	S=0;whl(1);return ans;
}

int co[50];bool flg;

void chkBin(int now,int c){
	if(co[now]){
		flg|=(co[now]!=c);return ;
	}
	co[now]=c;for(int i=1;i<=n;++i) if(G[now][i]) chkBin(i,((c-1)^1)+1);
}

int blk;

ll Ol1(){
	for(int i=1;i<=n;++i) if(!co[i]) chkBin(i,1),++blk;
	if(flg) return 0;
	else return 1ll<<blk;
}

int main()
{
	int m,x,y,isc=0;scanf("%d%d",&n,&m);
	while(m--) scanf("%d%d",&x,&y),G[x][y]=G[y][x]=1;
	for(int i=1;i<=n;++i){
		bool flg=0;for(int j=1;j<=n;++j) flg|=G[i][j];
		isc+=!flg;
	}
	printf("%lld",(1ll<<n)-2*NoAdj()+Ol1()-(1ll<<blk)+2*(1ll<<isc));
}
posted @   Albertvαn  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示