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;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    10月日常练习1题目描述
    普及组复赛历年考题
    9.3练习题7 子串乘积正负分类 题解
    9.3练习题6 旅行 题解
    9.3练习题4 语句解析 题解
    9.3练习题5 单词覆盖还原 题解
    unity
    矩阵快速幂
    点权和
    SCOI生日快乐
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2839.html
Copyright © 2011-2022 走看看