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

    Description:

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

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

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

    Hint:

    (n le 10^6)

    Solution:

    毒瘤组合数学题

    首先我们可以不考虑音乐顺序,最后答案除以m!即可

    易知所有非空子集数量为(2^n-1)

    设dp[i]为选了i段时的合法音乐种数

    为了满足出现次数为偶数,我们可以这样考虑:

    对于一种i-1段的音乐,第i段的集合一定由之前的奇数位唯一确定

    即方案数为(A_{2^n-1}^{i-1}) (想一想,为什么?)

    因为要考虑空集,所以就减去一个(dp[i-1])

    现在需要去重,我们钦定一个位置与i相同,于是需要减去方案数:

    [dp[i-2]*(i-1)*(2^n-1-(i-2)) ]

    这就完了

    [dp[i]=A_{2^n-1}^i-dp[i-1]-dp[i-2]*(i-1)*(2^n-i+1) ]

    其中 (dp[0]=1,dp[1]=0)

    #include <map>
    #include <set>
    #include <stack>
    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #define ls p<<1 
    #define rs p<<1|1
    using namespace std;
    typedef long long ll;
    const ll mxn=1e6+5,mod=1e8+7;
    ll n,m,tp,inv,a[mxn],dp[mxn];
    inline ll read() {
        char c=getchar(); ll x=0,f=1;
        while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
        while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
        return x*f;
    }
    inline ll chkmax(ll &x,ll y) {if(x<y) x=y;}
    inline ll chkmin(ll &x,ll y) {if(x>y) x=y;}
    
    ll qpow(ll a,ll b) 
    {
        ll res=1,bs=a;
        while(b) {
            if(b&1) res=1ll*res*bs%mod;
            bs=1ll*bs*bs%mod;
            b>>=1;
        }
        return res;
    }
    
    int main()
    {
        n=read(); m=read();
        inv=a[0]=1;
        for(ll i=2;i<=m;++i) inv=1ll*inv*i%mod;
        inv=qpow(inv,mod-2); tp=qpow(2,n)-1;
        for(ll i=1;i<=m;++i) a[i]=1ll*a[i-1]*(tp-i+1)%mod; dp[0]=1;
        for(ll i=2;i<=m;++i) {
            dp[i]=(a[i-1]-dp[i-1]+mod)%mod;
            dp[i]=(dp[i]-1ll*dp[i-2]*(i-1)%mod*(tp-(i-2)+mod)%mod+mod)%mod;
        }
        printf("%lld",1ll*dp[m]*inv%mod);
        return 0;
    }
    
    
  • 相关阅读:
    各种品牌电脑进入主板BIOS的方法(快捷键,按键)
    安装Oracle9i,遇到“File not found D: \oracle\ora92\ocs4j\admin\OCS4J.properties”
    关于远程桌面出现:“由于数据加密错误,这个会话将结束。请重新连接到远程计算机。”
    定时开关机
    Unix(Solaris) 常用基本命令和用户管理命令
    安装oracle9i时碰到缺少或无效口令提示
    ActiveX开发心得(转)
    oracle usermaneged recovery(三)
    用js实现改变随意改变div属性style的名称和值的结果
    Android_adb.exe的问题整理
  • 原文地址:https://www.cnblogs.com/list1/p/10526933.html
Copyright © 2011-2022 走看看