C. 集合计数
题目描述
输入格式
输出格式
样例
数据范围与提示
ab%c!=ab%c!!!卡了我好久。
这道题要用到组合数和容斥,因为交集为k的方案并不好求,但是交集至少为k的方案数比较好求,所以考虑容斥:
设提前选出来k个数,然后求交集至少为k+x时的方案数:(2^ 2(n-k-x)-1)*C(n-k,x),交集至少为k+x时,有2(n-k-x)个集合,则有2^ 2(n-k-x)种方案,但是要减去一个都不选的一种,乘C(n-k,x),然后容斥,对于x奇减偶加;
但是因为k是提前选出来的,以上并没有考虑k的方案,所以最后答案*C(n,k);
错误示范:
1 LL ans=0; 2 for(int i=0;i<=n-k;i++) 3 { 4 LL res=(inv(2,inv(2,n-k-i))-1)*C(n-k,i)%mod; 5 if(i%2)ans-=res; 6 else ans+=res; 7 ans%=mod; 8 }
ab%c!=ab%c!!!但是如果把循环顺序改一下,可以发现其实inv(2,inv(2,n-k-i))是每次平方的(inv为快速幂):
#include<iostream> #include<cstdio> #define mod 1000000007 #define LL long long using namespace std; int n,k; LL jc[1000010]; LL inv(LL a,LL b) { LL ans=1; while(b) { if(b&1)ans=ans*a%mod; a=a*a%mod; b=b>>1; } return ans%mod; } LL C(LL n,LL m) { if(!m)return 1; return jc[n] * inv(jc[m],mod-2) %mod * inv(jc[n-m],mod-2) %mod; } void init() { jc[0]=1; for(int i=1;i<=n;i++)jc[i]=jc[i-1]*i%mod; } signed main() { // freopen("in.txt","r",stdin); cin>>n>>k;init(); LL ans=0,pf=2; for(int i=n-k;i>=0;i--) { LL res=(pf-1)*C(n-k,i)%mod; pf=pf*pf%mod; if(i%2)ans-=res; else ans+=res; ans=(ans%mod+mod)%mod; } ans=(ans%mod+mod)%mod; ans=ans*C(n,k)%mod; cout<<(ans%mod+mod)%mod<<endl; }