ccpc 长春站 G

题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=5917]

题意:

给你一个无向图,问图中有多少个符合条件的集合?条件为这个集合里面存在一个子集(大小>=3),其中任意两个点之间相连或者都是孤立点。答案mod 1e9+7。

思路:

  • 先知道一个定理:Ramsey定理
    该定理简述为:6 个人中至少存在3人相互认识或者相互不认识。
    该定理等价于证明这6个顶点的完全图的边,用红、蓝二色任意着色,必然至少存在一个红色边三角形,或蓝色边三角形
    即:只要一个集合中有大于等于6个点,则一定符合条件
    所以对集合大小大于6的部分直接求
  • 小于6的部分暴力求解,n<=50,写5重循环也是不会超时的

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<sstream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<cstdlib>
#include<cmath>
using namespace std;
#define re register int
#define ull unsigned long long
#define ll long long
#define INF 0x3f3f3f3f
#define N 100010
#define lson rt<<1
#define rson rt<<1|1
#define mo 1000000007
#define lowbit(x) (x)&(-(x))
void FRE(){freopen("subsets.in","r",stdin);freopen("subsets.out","w",stdout);}
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ll)(ch-'0');ch=getchar();}
    return x*f;
}
ll ans;
int g[105][105],n,T,m;
ll ksm(ll a,ll b)
{
	ll tmp=1;
	while(b)
	{
		if (b&1) tmp=(tmp*a)%mo;
		b>>=1;
		a=(a*a)%mo;
	}
	return tmp;
}
ll niyuan(ll x)
{
	return ksm(x,mo-2);
}
ll C(int n,int m)
{
	ll tmp=1;
	for (ll i=1;i<=m;i++)
	{
		tmp=(tmp*(n-m+i)%mo)%mo;
		tmp=(tmp*niyuan(i)%mo)%mo;
	}
	return tmp;
}
bool OK(int i,int j,int k)
{
	if (g[i][j]&&g[i][k]&&g[j][k]) return true;
	if (g[i][j]+g[i][k]+g[j][k]==0) return true;
	return false;
}
bool OK2(int i,int j,int k,int p)
{
	if (OK(i,j,k)||OK(i,j,p)||OK(j,k,p)||OK(i,k,p))
		return true;
	return false;
}
bool OK3(int i,int j,int k,int p,int pp)
{
	if (OK2(i,j,k,p)||OK2(i,j,k,pp)||OK2(i,k,p,pp)||OK2(j,k,p,pp)||OK2(i,j,p,pp))
		return true;
	return false;
}
int main()
{
	T=read();
	for (int tt=1;tt<=T;tt++)
	{
		memset(g,0,sizeof(g));ans=0;
		n=read(),m=read();
		while (m--)
		{
			int x=read(),y=read();
			g[x][y]=g[y][x]=1;
		}
		if (n>=6) 
		{
			ans=ksm(2,n);
			for (int i=0;i<=5;i++)
				ans=(ans-C(n,i)+mo)%mo;
		}
		if (n>=3)
		{
			for (int i=1;i<=n;i++)
				for (int j=i+1;j<=n;j++)
					for (int k=j+1;k<=n;k++)
					if (OK(i,j,k)) ans=(ans+1)%mo;
		}
		if (n>=4)
		{
			for (int i=1;i<=n;i++)
				for (int j=i+1;j<=n;j++)
					for (int k=j+1;k<=n;k++)
						for (int p=k+1;p<=n;p++)
						if (OK2(i,j,k,p)) ans=(ans+1)%mo;
		}
		if (n>=5)
		{
			for (int i=1;i<=n;i++)
				for (int j=i+1;j<=n;j++)
					for (int k=j+1;k<=n;k++)
						for (int p=k+1;p<=n;p++)
							for (int pp=p+1;pp<=n;pp++)
							if (OK3(i,j,k,p,pp)) ans=(ans+1)%mo;
		}
		printf("Case #%d: %lld
", tt, ans%mo);
	}
	return 0;
}