ccpc 长春站 G - Instability(Ramsey定理)
题目链接:[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\n", tt, ans%mo);
}
return 0;
}