您的位置:首页 > 其它

【BZOJ-2839】集合计数 容斥原理 + 线性推逆元 + 排列组合

2016-05-10 09:36 447 查看

2839: 集合计数

Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 229 Solved: 120
[Submit][Status][Discuss]

Description

一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得
它们的交集的元素个数为K,求取法的方案数,答案模1000000007。(是质数喔~)

Input

一行两个整数N,K

Output

一行为答案。

Sample Input

3 2

Sample Output

6

HINT

【样例说明】
假设原集合为{A,B,C}
则满足条件的方案为:{AB,ABC},{AC,ABC},{BC,ABC},{AB},{AC},{BC}

【数据说明】
对于100%的数据,1≤N≤1000000;0≤K≤N;

Source

Solution

先手推了N=2~3,K的值,又推了推式子,大体上有所发现

首先N个元素中交集出现i个元素的的方案数为$C^{i}_{n}$

那么剩下$2^{n-i}$个其他集合,任选的方案总数为$2^{2^{n-1}-1}$种

最后统计答案$\sum_{k<=i<=N}(-1)^{i-k}*C^{i}_{n}*C^{k}_{i}*(2^{2^{n-i}-1})$

数据范围明显不能直接预处理C,所以先预处理阶乘和逆元再计算C即可

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int N,K;
const long long p=1e9+7;
#define maxn 1000010
long long inv[maxn],fac[maxn],ans;
void GetInv() {inv[1]=1;for (int i=2; i<=N; i++) inv[i]=(p-p/i)*inv[p%i]%p;}
void Prework() {inv[0]=1;for (int i=1; i<=N; i++)inv[i]=inv[i]*inv[i-1]%p;}
void GetFac() {fac[0]=1;for (int i=1; i<=N; i++) fac[i]=(long long)fac[i-1]*i%p;}
long long C(long long n,long long m) {return fac
*inv[m]%p*inv[n-m]%p;}
int main()
{
scanf("%d %d",&N,&K);
GetFac(); GetInv(); Prework();
for (long long i=N,tmp=2; i>=K; i--,tmp=tmp*tmp%p)
ans=(ans+((i-K&1?p-1:1)*C(N,i)%p*C(i,K)%p*(tmp+p-1)%p))%p;
printf("%lld\n",ans);
return 0;
}


丧心病狂的压代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: