ICode9

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

CF1221G Graph And Numbers

2022-07-31 18:33:29  阅读:207  来源: 互联网

标签:满足条件 return int Graph tot fa CF1221G Numbers col


written on 2022-05-06

鸣谢@uid13237的代码提供了我能理解的思路

这是一道计数题。

初见这题,有些束手无策,但是题目给出了三个限制,那么我们对于这种有限制的计数题,可以考虑容斥

大体思路就是容斥,想到这点,后面的大部分过程就很简单了,中间的过程可以参照这篇题解的,因为懒得打了

此题最闹心的部分在于对于没有 \(0\) 和没有 \(2\) 的部分,两种情况总数相同,下面我们不妨以没有 \(2\) 为例作分析。

这种情况不像其它情况那样有直观的方法计算。观察数据范围,40对于二进制枚举来说太大,对于 dp 来说又小得可怜,因此这里我们采用的方法是 折半搜索

折半搜索适应的数据范围一般是 40~45 左右,它的大体思路是枚举一半,统计另一半满足条件的数量。对于这道题,我们不妨将 \(n\) 个数分成两半,采用二进制思想,若二进制位为 \(1\) 则对应原位置填 \(1\) ,那么因为没有 \(2\) ,所以一条边连着的两端点就不能同时为 \(1\) 。

我们考虑在右部内,先单独统计出满足条件的那些部分。这里有一个显然的结论,即:如果一个二进制状态满足条件,那么它的子集均满足条件(因为 \(1 少了\))。于是统计出满足条件的状态后,我们用SOSdp来做一个高维前缀和来保证时间复杂度。

高维前缀和代码

for(int i=0;i<p;i++) for(int j=0;j<(1<<p);j++) if(j&(1<<i)) R[j]+=R[j^(1<<i)];

最后在左部内先筛选出满足条件的二进制状态,对应到右部中那些可以与其匹配的方案,直接累加答案即可。

中间的一个小细节:开一个变量 \(x\) ,若某一位为 \(1\) ,则说明这一位不能选,用全集减去 \(x\) ,即 \(x\) 的补集也就是满足条件的最大右部了。

代码

#include<bits/stdc++.h>
#define N 45
#define M 4005
using namespace std;
typedef long long ll;
int n,m,p,k,fa[N];
int get(int x){return x==fa[x]?x:fa[x]=get(fa[x]);}
bool flag;
int oth(int x){return x==1?2:1;}
int tot,ver[M],nxt[M],head[N];
void add_E(int x,int y){ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;}
int col[N];
void Col(int x)
{
	for(int i=head[x];i;i=nxt[i])
	{
		int y=ver[i];
		if(flag) return ;
		if(col[y])
		{
			if(col[y]!=oth(col[x])) flag=1;
			continue;
		}
		col[y]=oth(col[x]);
		Col(y);
	}
}
/*bool check(int s)
{
	memset(ans,0,sizeof(ans));
	for(int i=0;i<k;i++)
	{
		if(s&(1<<i)) ans[i+1]=1;
	}
	for(int x=1;x<=k;x++)
	{
		for(int i=head[x];i;i=nxt[i])
		{
			int y=ver[i];
			if(y>k) continue;
			if(ans[y]+ans[x]==0) return 0;
		}
	}
	return 1;
}
ll calc()
{
	ll res=0;
	for(int i=0;i<(1<<n);i++)
		if(check(i)) res++;
	return res;
}*/
bool w[N][N];
ll R[(1<<20)+5];
ll calc()
{
	for(int i=0;i<(1<<p);i++)
	{
		bool flag=0;
		for(int j=0;j<p;j++)
		{
			if((i&(1<<j))==0) continue;
			for(int l=0;l<p;l++)
			{
				if((i&(1<<l))==0) continue;
				if(w[l+k+1][j+k+1])
				{
					flag=1;
					break;
				}
			}
			if(flag) break;
		}
		if(!flag) R[i]=1;
	}
	for(int i=0;i<p;i++) for(int j=0;j<(1<<p);j++) if(j&(1<<i)) R[j]+=R[j^(1<<i)];//sosdp
	//一个数若可以,则它的子集一定都可以 
	ll res=0;
	for(int i=0;i<(1<<k);i++)
	{
		bool flag=0;
		for(int j=0;j<k;j++)
		{
			if((i&(1<<j))==0) continue;
			for(int l=0;l<k;l++)
			{
				if((i&(1<<l))==0) continue;
				if(w[l+1][j+1])
				{
					flag=1;
					break;
				}
			}
			if(flag) break;
		}
		if(flag) continue;
		int x=0;
		for(int j=0;j<k;j++)
		{
			if((i&(1<<j))==0) continue;
			for(int l=k;l<n;l++)
			{
				if(w[j+1][l+1]) x|=(1<<(l-k));
			}
		}
		res+=R[((1<<p)-1)^x];
	}
	return res;
}
int mark[N];
int main()
{
	scanf("%d%d",&n,&m);if(m==0) printf("0"),exit(0);
	k=n>>1,p=n-k;
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		fa[get(x)]=get(y);
		add_E(x,y),add_E(y,x);
		w[x][y]=w[y][x]=1;
	}
	int cnt=0,cnt2=0;
	for(int i=1;i<=n;i++)
	{
		int fx=get(i);
		if(fx==i) cnt++;
		mark[fx]++;
	}
	for(int i=1;i<=n;i++) if(mark[i]==1) cnt2++;
	for(int i=1;i<=n;i++)
	{
		if(!col[i]) col[i]=1,Col(i);
		if(flag) break;
	}
	ll now=calc();
//	printf("now=%lld\n",now);
	ll all=pow(2,n);
	all-=pow(2,cnt),all-=now*2,all+=(flag==0?pow(2,cnt):0),all+=pow(2,cnt2+1);
	printf("%lld",all);
}

标签:满足条件,return,int,Graph,tot,fa,CF1221G,Numbers,col
来源: https://www.cnblogs.com/Freshair-qprt/p/16537764.html

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

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

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

ICode9版权所有