洛谷傳送門
BZOJ傳送門
題目描述
衆所周知卡農是一種復調音樂的寫作技法,小余在聽卡農音樂時靈感大發,發明了一種新的音樂譜寫規則。他將聲音分成 個音階,並將音樂分成若干個片段。音樂的每個片段都是由 到 個音階構成的和聲,即從 個音階中挑選若干個音階同時演奏出來。爲了強調與卡農的不同,他規定任意兩個片段所包含的音階集合都不同。同時爲了保持音樂的規律性,他還規定在一段音樂中每個音階被奏響的次數爲偶數。現在的問題是:小余想知道包含 個片段的音樂一共有多少種。兩段音樂 和 同種當且僅當將 的片段重新排列後可以得到 。例如:假設
爲, 爲,那麼 與 就是同種音樂。由於種數很多,你只需要
輸出答案模 (質數)的結果。
輸入輸出格式
輸入格式:
從文件input.txt中讀入數據,輸入文件僅一行,具體是用空格隔開的兩個正整數和,分別表示音階的數量和音樂中的片段數。的數據滿足,的數據滿足,
的數據滿足。
輸出格式:
輸出文件 output.txt 僅包含一個非負整數,表示音樂的種數模 的結果。【輸入輸出樣例】
輸入輸出樣例
輸入樣例#1:
2 3
輸出樣例#1:
1
解題分析
題目無非是讓我們在選子集的時候滿足三個條件:
- 選的每個元素都出現偶數次
- 選的子集不爲空
- 不能選重複的子集
假設我們正在考慮計算。如果只要求滿足第一個要求, 直接確定個之前選的子集, 第個子集就唯一確定了, 所以這裏算出來的方案數是。
然後考慮爲空的限制, 發現當且僅當前個本身就滿足條件纔會導致第個爲空, 所以減去即可。
最後一個限制, 發現只可能是第個和前箇中的一個重複了, 那麼剩下的個就是合法的了, 也就是。 重複的一個的選法有, 然後重複的那個子集在個子集中有個不同的位置, 因此這一部分減去的貢獻是。
代碼如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <cstdlib>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MX 1005000
#define MOD 100000007
int A[MX], dp[MX];
int pw, n, m, fac = 1;
IN int fpow(R int base, R int tim)
{
int ret = 1;
W (tim)
{
if (tim & 1) ret = 1ll * ret * base % MOD;
base = 1ll * base * base % MOD, tim >>= 1;
}
return ret;
}
int main(void)
{
scanf("%d%d", &n, &m);
pw = (fpow(2, n) - 1 + MOD) % MOD;
A[0] = 1;
for (R int i = 1; i <= m; ++i) A[i] = 1ll * (pw - i + 1 + MOD) % MOD * A[i - 1] % MOD, fac = 1ll * fac * i % MOD;
dp[0] = 1, dp[1] = 0;
for (R int i = 2; i <= m; ++i) dp[i] = ((A[i - 1] - dp[i - 1] - 1ll * dp[i - 2] * (i - 1) % MOD * (pw - (i - 2)) % MOD) % MOD + MOD) % MOD;
printf("%lld\n", 1ll * dp[m] * fpow(fac, MOD - 2) % MOD);
}