• 《算法竞赛进阶指南》0x37容斥原理和Mobius函数 Devu和鲜花


    题目链接:https://www.acwing.com/problem/content/216/

    给出一个多重集,要求从里面取出m个,并且这m个构成的多重集不能是重复的,问有多少个这样的多重集。

    如果取出的数不大于任意一个同类集合的元素个数的话,直接通过C(n+m-1,n-1)即可求出取出的非重复的多重集数量,但是此时的m是很多大的,所以需要通过容斥原理枚举2^n个数来进行判断。统计一个数的二进制中1的个数和对应的1的位,就可以知道属于哪个容斥子元素以及其正负情况,注意特判一下等于0的情况,求阶乘的时候,可以运用lucas定理和逆元。

    代码:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int mod = 1000000007;
    typedef long long ll;
    const int maxn = 25;
    ll a[maxn];
    ll m;
    ll inv[25];
    ll ksm(ll a,ll b){
        ll ans=1;
        b%=(mod-1);
        while(b){
            if(b&1)ans=(ans*a)%mod;
            b>>=1;
            a=(a*a)%mod;
        }
        return ans;
    }
    ll C(ll y,ll x){//计算组合数C(y,x) 
        if(y<0 || x<0 || y<x)return 0;
        if(x==0 || y==0 || x==y)return 1;
        y%=mod;//lucas定理
        ll ans=1;
        for(int i=1;i<=x;i++){
            ans=ans*(y-x+i)%mod*inv[i]%mod;
        } 
        return ans;
    }
    int main(){
        for(int i=1;i<=20;i++)inv[i]=ksm(i,mod-2);
        ll ans=0;
        ll n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++)cin>>a[i];
        for(int x=0;x<1<<n;x++){
            if(x==0){
                ans=C(n+m-1,n-1)%mod;
            }else{
                ll t=n+m;
                int p=0;//计算集合的数量,为了判断容斥原理中的正负 
                for(int i=0;i<n;i++){//扫描n个集合,看是否包括在内 
                    if(x>>i & 1){
                        t-=a[i+1];
                        p++;
                    }
                }
                t-=(p+1);
                if(p&1)ans=(ans-C(t,n-1))%mod; 
                else ans=(ans+C(t,n-1))%mod; 
            }
        }
        cout<<(ans+mod)%mod<<endl;
        return 0;
    }
  • 相关阅读:
    ffmpeg视频操作记录
    frida定义线程写图片文件
    frida创建静态域
    frida创建字符串
    pyppeteer_stealth
    python ast
    最小的js编译器
    excel加双引号和逗号
    JUnit 单元测试方法中启用子线程的问题
    ctrip 开源 DAL 框架相关问题总结
  • 原文地址:https://www.cnblogs.com/randy-lo/p/13281185.html
Copyright © 2020-2023  润新知