zoukankan      html  css  js  c++  java
  • BZOJ2839 : 集合计数 (广义容斥定理)

    题目

    一个有 (N) 个 元素的集合有 (2^N) 个不同子集(包含空集),

    现在要在这 (2^N) 个集合中取出若干集合(至少一个),

    使得它们的交集的元素个数为 (K) ,求取法的方案数,答案模 (1000000007)

    ((1 le N le 10^6, 0 le K le N))

    题解

    又是一道 裸的 广义容斥定理 还没这道题难qwq

    广义容斥定理 (二项式反演) :

    [displaystyle b_k = sum_{i=k}^n inom i k a_i ]

    [Updownarrow ]

    [displaystyle a_k = sum_{i=k}^{n} (-1)^{i-k} inom i k b_i ]

    不难发现又是一个恰好 我们转化成至少就行了

    那么交集有至少 (i) 个集合的个数 (b_i) 就是

    [displaystyle b_i = inom n i 2^{2^{n-i}} ]

    一开始我以为后面那个直接是 (2^{n-i}) .... 没过样例搜了波题解... 发现是 (2^{2^{n-i}}) qwq

    为什么呢 我们这样考虑 当前枚举了一个大小为 (i) 交集后 还剩下 (n-i) 个元素

    每个元素有选和不选的两种方案 那么共有 (2^{n-i}) 个互不相同的集合

    那么每个集合我们又有选和不选两种方案 那么总共就是 (2^{2^{n-i}}) 种咯qwq

    然后套上去 答案就是

    [displaystyle mathrm{ans} = sum_{i=k}^{n} (-1)^{i-k} inom i k b_i ]

    代码

    #include <bits/stdc++.h>
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Set(a, v) memset(a, v, sizeof(a))
    using namespace std;
    
    inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
    inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
    
    inline int read() {
        int x = 0, fh = 1; char ch = getchar();
        for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
        for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
        return x * fh;
    }
    
    void File() {
    #ifdef zjp_shadow
    	freopen ("P2839.in", "r", stdin);
    	freopen ("P2839.out", "w", stdout);
    #endif
    }
    
    typedef long long ll;
    const ll Mod = 1e9 + 7;
    ll fpm(ll x, int power) {
    	ll res = 1;
    	for (; power; power >>= 1, (x *= x) %= Mod)
    		if (power & 1) (res *= x) %= Mod;
    	return res;
    }
    
    const int N = 1e6;
    ll fac[N + 100], ifac[N + 100], pow2[N + 100], ppow2[N + 100];
    void Init(int maxn) {
    	fac[0] = ifac[0] = pow2[0] = ppow2[0] = 1;
    	For (i, 1, maxn) fac[i] = fac[i - 1] * i % Mod, pow2[i] = pow2[i - 1] * 2 % Mod, ppow2[i] = ppow2[i - 1] * 2 % (Mod - 1);
    	ifac[maxn] = fpm(fac[maxn], Mod - 2);
    	Fordown (i, maxn - 1, 1) ifac[i] = ifac[i + 1] * (i + 1) % Mod;
    }
    
    ll ans = 0;
    
    ll C(int n, int m) {
    	if (n < 0 || m < 0 || n < m) return 0;
    	return fac[n] * ifac[m] % Mod * ifac[n - m] % Mod;
    }
    
    int main () {
    	File();
    	Init(N);
    	int n = read(), k = read();
    	For (i, k, n)
    		(ans += Mod + ((i - k) & 1 ? -1 : 1) * (C(i, k) * C(n, i) % Mod * fpm(2, ppow2[n - i]) % Mod)) %= Mod;
    	printf ("%lld
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    FreeMarker缓存处理
    freemarker入门实例与源码研究准备工作
    Freemarker常用技巧(二)
    Freemarker常用技巧(一)
    Hessian与Spring整合
    Hessian学习
    数组常见的面试题
    关于排序的实现
    Redis与Memcache的区别
    JDBC编程步骤
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/8719430.html
Copyright © 2011-2022 走看看