• BZOJ2339[HNOI2011]卡农——递推+组合数


    题目链接:

    [HNOI2011]卡农

    题目要求从$S={1,2,3……n}$中选出$m$个子集满足以下三个条件:

    1、不能选空集

    2、不能选相同的两个子集

    3、每种元素出现次数必须为偶数次

    我们考虑递推,设$f[i]$为选$i$个集合满足以上条件的方案数。

    考虑容斥:

    当确定了前$i-1$个集合后,要满足第三个条件的话,第$i$个集合是唯一确定的,所以总方案数为$A_{2^n-1}^{i-1}$。

    去掉第$i$个集合是空集的情况,如果第$i$个集合是空集,那么前$i-1$个集合一定合法,即方案数为$f[i-1]$。

    再去掉第$i$个集合与前面某个集合一样的情况,那么显然去掉这两个相同的集合后其他的$i-2$个集合也是合法的,第$i$个集合有$2^n-1-(i-2)$种选择,前面那个与第$i$个集合相同的集合的位置有$i-1$种选择,方案数为$(i-1)*(2^n-1-(i-2))*f[i-2]$。

    至此,我们可以得到递推式子$f[i]=A_{2^n-1}^{i-1}-f[i-1]-(i-1)*(2^n-i+1)*f[i-2]$。

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int n,m;
    int mod=100000007;
    ll f[1000010];
    ll g[1000010];
    ll quick(ll x,int y)
    {
        ll res=1ll;
        while(y)
        {
            if(y&1)
            {
                res=(res*x)%mod;
            }
            y/=2;
            x=(x*x)%mod;
        }
        return res;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        ll sum=quick(2ll,n);
        ll num=sum+mod-1;
        ll res=1ll;
        f[0]=1;
        f[1]=0;
        g[1]=num;
        for(int i=2;i<=m;i++)
        {
            f[i]=g[i-1]-f[i-1]+mod;
            f[i]-=((f[i-2]*(i-1))%mod*(sum-1-(i-2)))%mod;
            f[i]=(f[i]%mod+mod)%mod;
            g[i]=(g[i-1]*(num-i+1))%mod;
            res*=1ll*i;
            res%=mod;
        }
        ll ans=(f[m]*quick(res,mod-2))%mod;
        printf("%lld",ans);
    }
  • 相关阅读:
    28SQL 撤销索引、表以及数据库
    常见漏洞利用讲解
    6JavaScript 输出
    29_SQL ALTER TABLE 语句
    【首发】入门必看,性能测试指标详解,小白从零入门性能测试
    使用阿里云oss,在小程序端部分图片有时候显示,有时候不显示
    (0 , _auth.default) is not a function的问题
    uniapp开发小程序onReachBottom只触发一次
    Httprunner环境搭建
    4、vite创建vue项目
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/10422514.html
Copyright © 2020-2023  润新知