P3513 [POI2011] KON-Conspiracy
题目描述:
Byteotia的领土被占领了,国王Byteasar正在打算组织秘密抵抗运动。国王需要选一些人来进行这场运动,而这些人被分为两部分:一部分成为同谋者活动在被占领区域,另一部分是后勤组织在未被占领的领土上运转。但是这里出现了一个问题:
- 后勤组织里的任意两人都必须是熟人,以促进合作和提高工作效率。
- 同谋者的团体中任意两人都不能是熟人。
- 每一部分都至少要有一个人。国王想知道有多少种分配方案满足以上条件,当然也有可能不存在合理方案。
求方案数。
数据范围:
\(2\le n\le 5000\)
\(0\le k_i\le n-1\)
思路:
首先假设我们发现题目中是所有点都被两个集合所包含!!千万不要像我一样一开始理解为可以不包含满……
然后我们考虑这个题目的一个性质:如果想要选出一个集合 \(A\) 使得集合 \(A\) 的所有点之间都有连边,且 \(A\) 在全集 \(U\) 中的补集之间两两没有连边,则其一定满足 \(Deg=S+\binom{m}{2}\),其中 \(Deg\) 表示这个集合所有点的度数之和,\(S\) 表示整张图的边数,\(m\) 表示这个集合的大小
然后对于任意的一个子集,一定满足 \(Deg\le S+\binom{m}{2}\)
所以如果固定了 \(m\) 为多少,则只需要判断度数最大的 \(m\) 个点就可以了。
如果这 \(m\) 个点合法,我们不妨将这 \(m\) 个点中度数最小的点替换成其他未选择的且度数与其相同的点,假设一共有 \(a\) 个度数为最小值的点,其中 \(m\) 个点中选择了 \(b\) 个,所以方案数为 \(\binom{a}{b}\)
虽然这道题可以使用 \(2-SAT\) 来解决,但是显然能有更简单的方式肯定还是选择用简单的方式完成。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=5005;
const int mod=1e9+7;
int n;
int deg[maxn];
int fac[maxn],inv[maxn];
int qp(int x,int k){
int res=1;
while(k){
if(k&1)res=res*x%mod;
x=x*x%mod;
k>>=1;
}
return res;
}
void init(){
int N=maxn-5;
fac[0]=1;
for(int i=1;i<=N+1;i++)fac[i]=fac[i-1]*i%mod;
inv[N+1]=qp(fac[N+1],mod-2);
for(int i=N;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;
}
int C(int n,int m){
if(n<m||m<0)return 0;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
signed main(){
init();
cin>>n;
for(int i=1;i<=n;i++){
int k;cin>>k;
deg[i]+=k;
while(k--){
int x;cin>>x;
}
}
int S=0;
for(int i=1;i<=n;i++)S+=deg[i];
S/=2;
sort(deg+1,deg+n+1,greater<int>());
int sum=0,ans=0;
for(int i=1;i<=n;i++){
sum+=deg[i];
if(sum==S+i*(i-1)/2){
int a=0,b=0;
int pos=i;
while(pos>=1&°[pos]==deg[i])a++,b++,pos--;
pos=i+1;
while(pos<=n&°[pos]==deg[i])b++,pos++;
ans=(ans+C(b,a))%mod;
}
}
if(S==n*(n-1)/2)ans=(ans-1+mod)%mod;
cout<<ans<<endl;
return 0;
}