题目描述

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

输入格式

一行两个整数N,K

输出格式

一行为答案。

样例

样例输入

3 2

样例输出

6

数据范围与提示

样例说明

假设原集合为{A,B,C}

则满足条件的方案为:{AB,ABC},{AC,ABC},{BC,ABC},{AB},{AC},{BC}

数据说明

对于100%的数据,1≤N≤1000000;0≤K≤N;

思路:

  做数学题之前一定打个表。。。题解一贯作风,上来不打正解。。。

  前置知识:一个集合所有的子集个数为2^n每个元素是否出现,那么选子集的方案数为2^(2^n)-1每个集合是否出现,减一是因为一个集合都不选和选空集重了,少算一个。

  70%算法:DP真他娘的是啥都能干 ,我们不会算集合为n,交集为k的方案,那就设出来啊。。f[n][k]就是答案,f[n][k]=C(n,k)*f[n-k][0],意思是先选出来k个再选交集为0的,那么f[n][0]咋算,它可以从选所有的集合数2^(2^n)-1-f[n][k]得到,然而f[n][k]从之前的f[n-k][0]得到,没毛病后效性也没得,他娘的就用你打表了。。(此处借用WD大神代码)

 1 #include<cstdio>
 2 #define int long long
 3 const long long mod=1000000007;
 4 long long fac[1000005],inv[1000005],invv[1000005],x,y,n,k,f0[1000005],f[1000005];
 5 long long pow(long long b,long long t,long long modd,long long ans=1){
 6     for(;t;t>>=1,b=b*b%modd)if(t&1)ans=ans*b%modd;
 7     return ans;
 8 }
 9 signed main(){
10     scanf("%lld%lld",&n,&k);
11     fac[0]=inv[0]=invv[0]=f0[0]=invv[1]=inv[1]=fac[1]=1;f0[1]=2;
12     for(int i=2;i<=n;++i)fac[i]=fac[i-1]*i%mod,invv[i]=(-mod/i*invv[mod%i])%mod,inv[i]=inv[i-1]*invv[i]%mod;
13     for(int i=1;i<=n;++i){
14         f0[i]=(pow(2,pow(2,i,mod-1),mod)-1+mod)%mod;
15         for(int j=1;j<=i;++j)f[j]=fac[i]*inv[j]%mod*inv[i-j]%mod*f0[i-j]%mod,f0[i]=(f0[i]-f[j]+mod)%mod;
16         f[0]=f0[i];
17     }
18     printf("%lld\n",(f[k]+mod)%mod);
19 }
View Code

相关文章:

  • 2021-12-19
  • 2021-10-04
  • 2022-02-13
  • 2021-08-18
  • 2021-11-07
  • 2021-10-09
  • 2021-06-04
  • 2021-06-20
猜你喜欢
  • 2021-05-31
  • 2021-12-03
  • 2021-11-01
  • 2021-10-09
  • 2022-01-07
  • 2022-02-11
  • 2021-10-03
相关资源
相似解决方案