zoukankan      html  css  js  c++  java
  • [HNOI2011]卡农 题解

    题目描述

    众所周知卡农是一种复调音乐的写作技法,小余在听卡农音乐时灵感大发,发明了一种新的音乐谱写规则。他将声音分成 n 个音阶,并将音乐分成若干个片段。音乐的每个片段都是由 1 到 n 个音阶构成的和声,即从 n 个音阶中挑选若干个音阶同时演奏出来。为了强调与卡农的不同,他规定任意两个片段所包含的音阶集合都不同。同时为了保持音乐的规律性,他还规定在一段音乐中每个音阶被奏响的次数为偶数。现在的问题是:小余想知道包含 m 个片段的音乐一共有多少种。两段音乐 a 和 b 同种当且仅当将 a 的片段重新排列后可以得到 b。例如:假设 a

    为{{1,2},{2,3}},b 为{{3,2},{2,1}},那么 a 与 b 就是同种音乐。由于种数很多,你只需要

    输出答案模 100000007(质数)的结果。

    输入格式

    从文件input.txt中读入数据,输入文件仅一行,具体是用空格隔开的两个正整数n和m,分别表示音阶的数量和音乐中的片段数。20%的数据满足n,m≤5,50%的数据满足n,m≤3000,100%

    的数据满足n,m≤1000000。

    输出格式

    输出文件 output.txt 仅包含一个非负整数,表示音乐的种数模 100000007 的结果。

    很强的容斥题。

    首先将题意化简一下:从集合$S={1,2,3,...,n}$中选出$m$个子集,满足非空选出的子集不能相同保证${1,2,3...n}$每个元素出现次数为偶数

    题目里给出的同种音乐的定义很令人烦躁,所以我们化无序为有序,先使用排列数计算,算出最后结果再除以$m!$(当然直接用组合也可以?)。

    然后考虑转移。定义$f[i]$为转移到第$i$个子集,满足所有条件的方案数。如果前$i-1$个子集已经确定,那么根据每个元素出现次数为偶数这条性质,我们就可以确定第$i$个子集(只能选前$i-1$个里出现奇数次的元素)。总的方案数为$A_{2^n-1}^{i-1}$。

    这里面包括了许多不满足非空和不相同这两个条件的集合。考虑容斥掉,如果第$i$个子集为空,那么前$i-1$个子集也是一个合法的方案。所以这部分方案数为$f[i-1]$。

    还需要去掉存在相同子集的情况。如果第$i$个子集和第$j$个子集重复,那么去掉第$i$个和第$j$个,剩下$i-2$个也是合法方案,数量为$f[i-2]$。此时第$i$个子集有$2^n-1-(i-2)$种方案,和$i$相同的那个子集的位置有$i-1$个,所以这部分的方案数为$f[i-2] imes (i-1) imes (2^n-1-(i-2))$。

    $f[i]=A_{2^n-1}^{i-1}-f[i-1]-f[i-2] imes (i-1) imes (2^n-1-(i-2))$

    初始化$f[0]=1$。

    //实名diss某川 明明当年做题的时候跟我说了题解 还不上去讲
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    #define re register
    const int mod=1e8+7,N=1000010;
    ll f[N],fac,a[N],side,n,m;
    ll qpow(ll x,ll y)
    {
        ll res=1;
        while(y)
        {
            if(y&1)res=res*x%mod;
            x=x*x%mod;
            y>>=1;
        }
        return res;
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cin>>n>>m;
        fac=a[0]=f[0]=1;
        for(re int i=2;i<=m;i++)fac=fac*i%mod;
        fac=qpow(fac,mod-2);
        side=qpow(2,n)-1;
        for(re int i=1;i<=m;i++)a[i]=a[i-1]*(side-i+1)%mod;
        for(re int i=2;i<=m;i++)
        {
            f[i]=a[i-1]-f[i-1];
            f[i]-=(f[i-2]*(i-1)%mod*(side-i+2))%mod;
            f[i]=(f[i]%mod+mod)%mod;
        }
        cout<<f[m]*fac%mod<<endl;
        return 0;
    }
    View Code
  • 相关阅读:
    数据库的查询笔记
    数据库设计第3级的阶梯:构建表
    数据库使用学习笔记
    index()
    面向对象写法的拖拽
    js面向对象
    laytpl.js 模板使用记录
    面试总结
    JS的基础类型与引用类型
    jQuery两种扩展插件的方式
  • 原文地址:https://www.cnblogs.com/Rorschach-XR/p/11330388.html
Copyright © 2011-2022 走看看