[BZOJ 2839] 集合计数
题目描述
输入格式
输出格式
样例
数据范围与提示
思路:
做数学题之前一定打个表。。。题解一贯作风,上来不打正解。。。
前置知识:一个集合所有的子集个数为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 }
100%算法:乖乖打正解,先选k个,n-=k,现在我们要求的就是集合为n,交集为0的方案,这时候我们重新找一个集合的定义(本人思路卒于此),集合A表示包含一个确定元素a的方案的总和,那么会有n个集合向像花一样绽放,有各种交集例如图中中间二级重叠不包含E部分表示交集恰有2个的方案。那么我们就要求一级部分的大小(就是要求的无交集的方案),仿佛嗅到了一丝容斥的气息。。。。交集为0=总-(>=1)+(>=2)-.......
ans=Σ(-1)^i*C(n,i)*(2^(2^(n-i))-1);
PS:所谓的次方的次方可以用递推,当然你打两层快速幂我也没办法。。
#include<iostream> #include<cstdio> #include<cmath> using namespace std; const int mn=1e6+20,mod=1e9+7; int f[mn],fac[mn],inv[mn],n,k; int qpow(int x,int k) { int ans=1; for(;k;k>>=1,x=1ll*x*x%mod) if(k&1) ans=1ll*ans*x%mod; return ans; } long long C(int n,int m) {return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;} int main() { scanf("%d%d",&n,&k);fac[0]=1; for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod; inv[n]=qpow(fac[n],mod-2); for(int i=n;i;i--) inv[i-1]=1ll*inv[i]*i%mod; long long ans=0,tmp=C(n,k),tp=1;n-=k; int op=(n&1)?-1:1; for(int i=n;~i;i--) { ans=(ans+op*C(n,i)*tp%mod)%mod; tp=(tp*tp%mod+2*tp%mod)%mod; op=-op; } ans=ans*tmp%mod; printf("%lld\n",(ans+mod)%mod); } //for sigma i=0->n C(n,i)(2^(2^(n−i))−1) /* g++ 1.cpp -o 1 ./1 4 1 */
Zeit und Raum trennen dich und mich.时空将你我分开。