bzoj2839 关于组合数的若干yy

考虑容斥,最后的答案为ni=k(1)ikCinCki(22ni1)
实际上-1和最后一部分并不需要解释,然后我们看看如何证明这个的正确性。


辣鸡的证明

关于普通的容斥原理,我们可以用二项式定理yy
当我们发现对于每一个交集大小为n的选法,他最后被统计的次数是这个.
ni=k(1)ikCinCki由于我菜,就上uoj群问了一发,钊神说这似乎是证明二项式反演的啥啥玩意儿orz.
我们根据组合数的公式,有
CinCki=CknCiknk

那么
ni=k(1)ikCinCki=0  (k<n)
=ni=k(1)ikCknCiknk=0  (k<n)
=Cknni=k(1)ikCiknk=0  (k<n)
=Ckn(11)nk
考虑这个式子,当n>k的时候,他=0,当n=k时,他=1.这就说明我们最后只算了交集为k的情况.


丑陋的代码

#include<cstdio>
#include<algorithm>

const int P = 1e9 + 7, N = 1e6 + 8;

int n, k;

typedef long long ll;

ll la = 2, res, jc[N], jc_rev[N];

ll C (int n, int m) {
    if (m > n) return 0;
    return jc[n] * jc_rev[m] % P * jc_rev[n - m] % P;
}

ll quick_power (ll A, int B) {
    ll C = 1;
    for (; B; B >>= 1, A = A * A % P) if (B & 1) C = C * A % P;
    return C;
}

int main () {
    scanf ("%d%d", &n, &k);
    jc[0] = 1; for (int i = 1; i <= n; ++i) jc[i] = jc[i - 1] * i % P;
    jc_rev[n] = quick_power (jc[n], P - 2);
    for (int i = n - 1; ~i; --i) jc_rev[i] = jc_rev[i + 1] * (i + 1) % P;
    for (int i = n, fu = 1; i >= k; --i, fu = -fu, la = la * la % P) { 
        res = ((la - 1) * C (i, k) % P * C (n, i) - res) % P;
    }
    printf ("%lld\n", (res + P) % P);
    return 0;
}
posted @ 2017-02-27 09:40  DraZxlnDdt  阅读(181)  评论(0编辑  收藏  举报