zoukankan      html  css  js  c++  java
  • 【BZOJ2839】集合计数(广义容斥)

    点此看题面

    • 一个(n)个元素的集合有(2^n)个不同的子集。
    • 要求从这些子集中选出至少一个集合,使得选出集合的交集大小恰好为(k),求方案数。
    • (nle10^6)

    广义容斥

    对于这种恰好为(k)的显然想到容斥,去设(f_i)为交集大小大于等于(i)的方案数,根据广义容斥的式子就可以发现:

    [ans=sum_{i=k}^n(-1)^{k-i}C_i^kf_i ]

    于是只要考虑如何快速计算(f_i),首先有个选出(i)个数的方案数(C_n^i)

    那么选出的所有集合都必须包含这(i)个数,剩下(n-i)个数可以任选,因此总共有(2^{n-i})个包含这(i)个数的集合。

    然后我们可以在这些集合中选出至少一个,最终得到:

    [f_i=C_n^i imes 2^{2^{n-i}}-1 ]

    此时已经可以(O(logn))计算每个(f_i)了,但事实上我们可以发现:

    [2^{2^{n-i}}=2^{2^{n-i-1} imes 2}=(2^{2^{n-(i+1)}})^2 ]

    所以我们只要维护(2^{2^{n-i}})并从大到小枚举(i),便可实现(O(n))了。

    代码:(O(n))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 1000000
    #define X 1000000007
    #define C(x,y) (1LL*Fac[x]*IFac[y]%X*IFac[(x)-(y)]%X)//组合数
    using namespace std;
    int n,k,Fac[N+5],IFac[N+5];
    I int QP(RI x,RI y,CI p) {RI t=1;W(y) y&1&&(t=1LL*t*x%p),x=1LL*x*x%p,y>>=1;return t;}
    int main()
    {
    	RI i,p;for(scanf("%d%d",&n,&k),Fac[0]=i=1;i<=n;++i) Fac[i]=1LL*Fac[i-1]*i%X;//预处理阶乘
    	for(IFac[i=n]=QP(Fac[n],X-2,X);i;--i) IFac[i-1]=1LL*IFac[i]*i%X;//预处理阶乘逆元
    	long long t=0;for(p=2,i=n;i>=k;p=1LL*p*p%X,--i) t+=((i-k)&1?X-1LL:1LL)*C(i,k)%X*C(n,i)%X*(p-1)%X;//广义容斥
    	return printf("%d
    ",(t%X+X)%X),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    CMS .NET 程序框架 从2.0/3.5升级到4.0 版本后 需要调整的地方
    配置信息
    修改SQL Server 2005 数据库文件名字
    生成一行html
    安卓模拟器研究-root
    Win8.1屏幕亮度自动调节关闭方法
    ORA-01012:not logged on的解决办法
    ORA-00845: MEMORY_TARGET not supported on this system
    Linux vmstat命令实战详解
    Linux操作系统下Oracle主要监控工具介绍
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2839.html
Copyright © 2011-2022 走看看