组合数学
鸽巢原理典型题目:
吃糖果:
很奇妙的思路找糖果最多的作为隔板,假设有N个隔板,剩余的糖果S必须有S>=N才有唯一解(可以分类讨论一下很有趣,在S<N时,无解)
鸽巢原理是Ramsey定理的一个特例,下面是Ramsey的知识:
Ramsey定理:世界上任意3个人之间,总有3个人互相认识或3个人互相不认识
证明:假设6个人,a,b,c,d,e,f,有a出发有,ab,ac,ad,ae,af,这五个线段,他们之间认识用红色标记,不认识用蓝色标记。
由抽屉定理得,这5条线段中至少有3个同色。不妨设ab,ac,ad为红色,讨论b,c,d三者的情况,若bc,bd,cd三者至少一个为红色,则这6个人至少3人认识,结论成立。若bc,bd,cd三者都为蓝色,则这6个人存在3个人互不认识,结论成立。😏
该定理等价于证明这6个顶点的完全图的边,用红、蓝二色任意着色,必然至少存在一个红色边三角形,或蓝色边三角形
下面有个比赛题目hdu5917题意:
无向图,求这个图有多少个点的集合,每个集合至少包含一个关于这个图的独立集或者团。并且集合的元素个数要大于等于3。(其实看到这种题还停留到只能想到并查集😅)
思路:运用Ramsey定理,如果该题城市数超过6个(包含6)一定存在这样的独立集,那么就直接求组合数就好了;如果城市数少于6个,当然还要大于等于3,也就是如果城市数是3,4,5就直接一一枚举。当城市数大于6时:(C(n,0)+C(n,1)…C(n,n)=pow(2,n),减去0到5。
#include <iostream>
#include <cstring>
#include <stdio.h>
#define ll long long
using namespace std;
bool v[55][55];
ll c[55][55];
const int mod=1e9+7;
ll ans;
int n,m;
void init()//用到杨辉三角,二项式
{
c[0][1]=c[1][1]=1;
for(int i=2;i<=m;i++)
for(int j=0;j<=i;j++){
if(j==0) c[j][i]=1;
else c[j][i]=(c[j][i-1]+c[j-1][i-1])%mod;
}
}
ll pow(ll a,ll b)//快速幂(前一个博客)
{
ll res=1;
while(b)
{
if(b&1)
res=res *a%mod;
a=a*a%mod;
b=b>>1;
}
return res;
}
bool judge(int a,int b,int c)
{
if(v[a][b]&&v[a][c]&&v[b][c]) return true;
if(!v[a][b]&&!v[a][c]&&!v[b][c]) return true;
return false;
}
bool judge(int a,int b,int c,int d)
{
if(judge(a,b,c)) return true;
if(judge(b,c,d)) return true;
if(judge(a,b,d)) return true;
if(judge(a,c,d)) return true;
return false;
}
bool judge(int a,int b,int c,int d,int e)
{
if(judge(a,b,c,d)) return true;
if(judge(a,b,c,e)) return true;
if(judge(a,c,d,e)) return true;
if(judge(b,c,d,e)) return true;
if(judge(a,b,d,e)) return true;
return false;
}
int main()
{
int t,tt=0;
cin>>t;
while(t--)
{
tt++;
cin>>m>>n;
memset(v,false,sizeof(v));
while(n--)
{
int i,j;
cin>>i>>j;
v[i][j]=true;
v[j][i]=true;
}
ans=0;
if(m>=6)
{
init();
ans+=pow(2,m);
for(int i=0;i<=5;i++)
{
ans-=c[i][m];
ans+=mod;//
ans%=mod;
}
}
for(int i=1; i<=m; i++)
for(int j=i+1; j<=m; j++)
for(int k=j+1; k<=m; k++)
if(judge(i,j,k)) ans++;
for(int i=1; i<=m; i++)
for(int j=i+1; j<=m; j++)
for(int k=j+1; k<=m; k++)
for(int l=k+1; l<=m; l++)
if(judge(i,j,k,l)) ans++;
for(int i=1; i<=m; i++)
for(int j=i+1; j<=m; j++)
for(int k=j+1; k<=m; k++)
for(int l=k+1; l<=m; l++)
for(int u=l+1; u<=m; u++)
if(judge(i,j,k,l,u)) ans++;
ans%=mod;
printf("Case #%d: %lld\n",tt,ans);
}
return 0;
}
🍔🍟🌭