传送门
首先考虑固定k个元素,方案为 Ckn
还剩下 2n−k 个集合,可以任选若干个集合 C12n−k+C22n−k+..+C2n−k2n−k=22n−k
但是这样选出来的有可能有不合法的,交集大小可能大于k,所以要减去k+1,加上k+2…
就是个容斥了
设 f(k)=Ckn∗(22n−k)
那么答案应该为 f(k)∗Ckk−f(k+1)∗Ckk+1+f(k+2)∗Ckk+2...f(n)∗Ckn
容斥系数我刚开始是找的规律,但是后来发现,因为第一项即使不考虑交集大小可以大于k的情况,也会多出来很多重复的情况,因为选取的k个元素和选取的集合可能是重复的。那么后面进行容斥的时候就要利用组合数将重复的去掉
对于 22n−k 的求法,可以变成 22n−k mod φ(p) 。根据欧拉定理 aφ(p)≡1(modp),(a,p)=1 ,可以得出 an−φ(p)≡an(modp) ,而这里的a和p显然是互质的。
这题如果直接暴力的话会非常慢。比较科学的做法是线性推出逆元然后再预处理阶乘和逆元的阶乘,以及2的整数次幂。这样速度就比较优秀了
线性推逆元方法 inv(i)=(p−p/i)∗inv(p%i)%p ,初始 inv(1)=1
#include
#include
#include
#include
#include
using namespace std;
#define Mod 1000000007
#define N 1000005
#define LL long long
int n,k,f;
LL mi[N],mul[N],inv[N],ans;
void calc(int n)
{
mul[0]=1;
for (int i=1;i<=n;++i) mul[i]=mul[i-1]*(LL)i%Mod;
inv[1]=1;
for (int i=2;i<=n;++i) inv[i]=inv[Mod%i]*(Mod-Mod/i)%Mod;
inv[0]=1;
for (int i=1;i<=n;++i) inv[i]=inv[i]*inv[i-1]%Mod;
mi[0]=1;
for (int i=1;i<=n;++i) mi[i]=mi[i-1]*(LL)2%(Mod-1);
}
LL fast_pow(LL a,int p,int mod)
{
LL ans=1;
for (;p;p>>=1,a=a*a%mod)
if (p&1)
ans=ans*a%mod;
return ans;
}
LL C(int n,int m)
{
if (m>n) return 0;
return mul[n]*inv[m]%Mod*inv[n-m]%Mod;
}
int main()
{
scanf("%d%d",&n,&k);
calc(n);
f=1;
for (int i=k;i<=n;++i,f=-f)
ans+=C(n,i)*(fast_pow(2,mi[n-i],Mod)-1)%Mod*C(i,k)%Mod*f;
ans=(ans%Mod+Mod)%Mod;
printf("%lld\n",ans);
}