1. 程式人生 > >[HNOI2011]卡農

[HNOI2011]卡農

sub 強調 一行 為什麽 輸出格式 保持 n) 規則 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 的結果。【輸入輸出樣例】

輸入輸出樣例

輸入樣例#1:
2 3
輸出樣例#1:
1

說明

樣例解釋:音樂為{{1},{2},{1,2}}

首先題目裏說是無序的,但是不要管它,我們先把它看成有序的,最後除以一個m!即可。我們考慮補集轉換,首先所有的子集個數應該是2n?1

,我們定義f[i]為挑選i個片段的合法的方案數,此時總數應該是A(2n?1,i?1)(排列數)。為什麽是i?1而不是i呢?因為要保證總數是偶數,也就是說如果你確定了i?1個片段第i個片段也就確定了。而這樣肯定多算了,具體來說有兩部分:
1、如果前i?1個已經合法,那麽第i個就是空集,這樣肯定不合法,所以要減去f[i?1]
2、如果根據前i?1個確定出來的第i個集合和前面的某一個重復,這樣肯定是不合法的。
因為考慮順序,所以那個和第i個重復的集合有i?1種位置,對於每種位置,當前的總數偶數去掉兩個數之後還是偶數,所以剩下其他數的方案數為f[i?2]。然後我們需要算出有多少種可能重復的方案,因為我們已經確定了(i?2)
個位置,所以方案數為(2n?1?(i?2))。所以總體的方程就是:f[i]=A(2n?1,i?1)?f[i?1]?f[i?2]?(2n?1?(i?2))?(i?1)
最後在乘上一個m!關於mod的逆元即可。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 long long Mod=100000007,s;
 7 long long n,m;
 8 long long pre[1000001],A[1000001],f[1000001];
 9 long long qpow(long long x,int y)
10 {
11     long long res=1;
12     while (y)
13     {
14         if (y%2==1)
15         {
16             res=(res*x)%Mod;
17         }
18       x=(x*x)%Mod;
19       y/=2;
20     }
21     return res;
22 }
23 int main()
24 {long long i;
25     cin>>n>>m;
26      s=(qpow(2,n)-1+Mod)%Mod;
27       //p=qpow(2,n)%Mod;
28      pre[0]=1;
29      for (i=1;i<=m;i++)
30      {
31             pre[i]=(pre[i-1]*(s-i+1+Mod)%Mod)%Mod;
32      }
33      A[1]=1;
34      for (i=2;i<=m;i++)
35      A[i]=((Mod-Mod/i)*A[Mod%i]+Mod)%Mod;
36       f[1]=0;f[2]=0;
37     for (i=3;i<=m;i++)
38     {
39         f[i]=(pre[i-1]+Mod)%Mod;
40         f[i]=(f[i]-f[i-1]+Mod)%Mod;
41         if (f[i]<0) f[i]+=Mod;
42         f[i]=(f[i]-((f[i-2]*(i-1)%Mod)*((s-i+2+Mod)%Mod))%Mod+Mod)%Mod;
43         if (f[i]<0) f[i]+=Mod;
44     }
45     for (i=1;i<=m;i++)
46     f[m]=(f[m]*A[i])%Mod;
47 cout<<f[m];
48 }

[HNOI2011]卡農