bzoj2839 集合计数(容斥+组合)
集合计数
题目描述
输入格式
输出格式
样例
数据范围与提示
一道很好的数论题(反正我看题解才做出来的 %%%%*ZJ https://www.cnblogs.com/zj75211/p/8029343.html) 我有点菜,各种错误。
思路很奇妙,第一步:首先选出来C(n,k)个数,第二步:然后我们要做的就是让剩余集合交集为空。注意,我们选的剩余集合是除了C以外还选了什么(C上再加这个集合)
解释一下
例如 3 1
假设我们通过第一步选出来的数为A
而第二步我们选出来 C,B
那么我们最终选的集合为AB ,AC
再比如我们第二步选 空集,C
那么我们最终选出来的集合为 A,AC
既然如此我们需要求的就是所有满足的情况
正确性验证被我鸽了。。。。。。
然后我们要做的就是求出方案
设f[i]为当前集合至少选出i个数的方案数
设m为n-i(即剩余个数)
对于剩余n-i个数可以构成$2^{(n-i)}$个集合
而对于这些集合每个都可以进行选或不选(即$2{^{2^{(n-i)}}}$)种情况
而不能都不选(对于选了空集也是一种)
则
$f[i]=C(n,i)*(2{^{2^{n-i}}}-1)$
显然我们算多了,然后我们需要容斥掉容斥系数,首先我们发现在所有f[k+1]中我们都多算了C(k+1,k)次
例如我们固定了四个数ABCD
我们在k+1中每种都多算了一次
ABC D==ACD B==ABD C==BCD A
得到对于f[k+1]:-C(k+1,k)
同理f[k+2]:+C(k+2,k+1)*C(k+1,k)==C(k+2,k)
依次类推
得到
for(ll i=k;i<=n;i++) ans=(C(i,k)*f[i]%p*((i-k)&1?-1:1)+ans)%p;
实际上就类似于多项容斥
而且这题因为N<=1000000
如果算C现算肯定不行,考虑到用多次,打表(阶乘及阶乘逆元),但求逆元带log仍然超时
所以还要线性求逆元
ni[n]=meng(jie[n],p-2); for(ll i=n-1;i>=1;i--)ni[i]=ni[i+1]*(i+1)%p;
理解一下逆元实际上就是1/?
而$1/(n+1)!*(n+1)$其实就是$1/(n)! $就求出来逆元了
两个注意点
:for(ll i=1;i<=maxn;i++) ermi[i]=2*ermi[i-1]%(p-1);//2^ermi[i]%p!=2^ermi[i]%p调两节课
:for(ll i=0;i<=n;i++)又一节半课,实在是菜
:ermi[0]又二十分钟,
除了第一个是稍微有一点思维的错,别的都是低错。实不应该
首先关于为什么mod (p-1)而不是mod (p)
这其实要推出来,首先我们必须知道${2^{2^i mod p}}{!=2^{2^i}{mod p}}$
怎么办 设${2^i}==(kφ(p)+t)$,则原式就为$(2^{k*φ(p)}*2^t) {mod p}$
根据欧拉定理
$2^{φ(p)} mod p$同余于1,${2^i}==kφ(p)+t$t就等于$2^i mod φ(p)$,又φ(质数)==p-1
故%(p-1)而非%p
以下依然是本人丑陋的还带着调试的代码
1 #include<bits/stdc++.h> 2 #define ll long long 3 #define A 1100000 4 #define maxn 1000010 5 #define p 1000000007 6 using namespace std; 7 ll m,n,k,f[A],jie[A],ermi[A],ans,ni[A]; 8 inline ll read() 9 { 10 ll f=1,x=0;char c=getchar(); 11 while(!isdigit(c)){if(c=='-') f=-1;c=getchar();} 12 while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();} 13 return f*x; 14 } 15 ll meng(ll x,ll k) 16 { 17 ll ans=1; 18 for(;k;k>>=1,x=x*x%p) 19 if(k&1) 20 ans=x%p*ans%p; 21 return ans; 22 } 23 ll C(ll n,ll m) 24 { 25 if(m==0) return 1; 26 if(m>n) return 0; 27 else return (jie[n]*ni[m]%p*ni[n-m])%p; 28 } 29 void init() 30 { 31 n=read(),k=read(); 32 m=n-k; 33 jie[0]=1;ni[0]=1;ermi[0]=1; 34 for(ll i=1;i<=maxn;i++) ermi[i]=2*ermi[i-1]%(p-1);//2^ermi[i]%p!=2^ermi[i]%p 35 for(ll i=1;i<=n;i++) jie[i]=jie[i-1]*i%p; 36 ni[n]=meng(jie[n],p-2); 37 for(ll i=n-1;i>=1;i--)ni[i]=ni[i+1]*(i+1)%p; 38 for(ll i=0;i<=n;i++) f[i]=C(n,i)%p*(meng(2,ermi[n-i])%p-1)%p; 39 } 40 int main() 41 { 42 // freopen("test.in","r",stdin);freopen("vio.out","w",stdout); 43 ans=0; 44 init(); 45 for(ll i=k;i<=n;i++) 46 ans=(C(i,k)*f[i]%p*((i-k)&1?-1:1)+ans)%p; 47 cout<<(ans%p+p)%p<<endl; 48 }
以及对拍
1 #include<bits/stdc++.h> 2 int main() 3 { 4 while(true) 5 { 6 system("./mkd"),puts("mkd runs out"); 7 system("./std"),puts("std runs out"); 8 system("./vio"),puts("vio runs out"); 9 if(system("diff std.out vio.out")) while(true); 10 puts(""); 11 } 12 return 0; 13 }
嗯,完了