zoukankan      html  css  js  c++  java
  • 集合计数,容斥原理

    一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得它们的交集的元素个数为K,求取法的方案数,答案模1000000007。(是质数喔~)

    输入格式

    一行两个整数N,K

    输出格式

    一行为答案。

    样例

    样例输入

    3 2
    

    样例输出

    6
    


    析:要求交集个数为K,直接求显然不好做,考虑用容斥原理,交集个数为K=交集至少为K-交集至少为(K+1)+交集至少为(K+2)—......
      若交集至少为K,则取出K个元素,还剩下(N_K)个元素,剩下的元素共有2^(n-k)-1种集合(因为不能是空集),把每个集合看成一种元素,则共有2^(2^((n-k)-1))种选择方案,则方案数为C(n,k)*2^(2^((n-k)-1)),以此类推
      注意,若交集至少为i,则重复的方案数为C(i,k)!!!

    知识点:lucas定理:C(n,m)%p=C(n/p,m/p)*C(n%p,m%p);
        C(n,m)=c!*inv[m!]*inv[(n-m)!]
        线性求阶乘逆元:inv[i]=(inv[i+1]%mo*(i+1)%mo)%mo,因为逆元相当于求倒数,1/(n+1)!*(n+1)=n!;
        A^x = A^(x % Phi(C) + Phi(C)) (mod C)

    代码如下:
    ```cpp
    #include<bits/stdc++.h>
    #define ll long long
    #define re register int
    #define mo 1000000007
    #define N 20000010
    #define phi 1000000006
    using namespace std;
    ll n,k,ans;
    ll jc[N],f[N],inv[N];
    bool p;
    ll ksm(ll d,ll z)
    {
        ll out=1;
        while(z)
        {
            if(z&1)
                out=out*d%mo;
            d=d*d%mo;
            z>>=1;
        }
        return out%mo;
    }
    ll suan(ll n,ll m)
    {
        return jc[n]%mo*inv[m]%mo*inv[n-m]%mo;
    }
    ll lucas(ll n,ll m)
    {
        if(m>n)
            return 0;
        if(n==0||m==0)
            return 1;
        return lucas(n/mo,m/mo)*suan(n%mo,m%mo)%mo;
    }
    int main()
    {
        scanf("%lld%lld",&n,&k);
        jc[0]=1;
        for(re i=1;i<=n;i++)
            jc[i]=jc[i-1]*i%mo;
        f[0]=1;
        for(re i=1;i<=n;i++)
            f[i]=(f[i-1]*2%phi+phi)%phi;
        inv[n]=ksm(jc[n],mo-2);
        for(re i=n-1;i>=0;i--)
            inv[i]=(inv[i+1]%mo*(i+1)%mo)%mo;
        for(re i=k;i<=n;i++)
        {
            if(p==0)
            {
                ans=(ans+lucas(i,k)%mo*lucas(n,i)%mo*(ksm(2,f[n-i])-1)%mo)%mo;
                p=1;
            }
            else
            {
                ans=(ans-lucas(i,k)%mo*lucas(n,i)%mo*(ksm(2,f[n-i])-1)%mo)%mo;
                p=0;
            }
        }
        printf("%lld",(ans+mo)%mo);
        return 0;
    }

    ```
  • 相关阅读:
    面试题
    面试题-基础篇(1)
    Windows定时器学习
    C++中类的多态与虚函数的使用
    Win32 SDK 编程开始, 创建窗口, 消息的处理, 消息循环
    线程同步(互斥锁与信号量的作用与区别)
    控制台程序实现暂停功能
    Linux下gcc编译生成动态链接库*.so文件并调用它
    Linux下安装ActiveMQ CPP
    activemq Linux下的编译
  • 原文地址:https://www.cnblogs.com/WindZR/p/14654187.html
Copyright © 2011-2022 走看看