[Luogu P3214] [BZOJ 4339] [HNOI2011]卡農

洛谷傳送門

BZOJ傳送門

題目描述

衆所周知卡農是一種復調音樂的寫作技法,小余在聽卡農音樂時靈感大發,發明了一種新的音樂譜寫規則。他將聲音分成 nn 個音階,並將音樂分成若干個片段。音樂的每個片段都是由 11nn 個音階構成的和聲,即從 nn 個音階中挑選若干個音階同時演奏出來。爲了強調與卡農的不同,他規定任意兩個片段所包含的音階集合都不同。同時爲了保持音樂的規律性,他還規定在一段音樂中每個音階被奏響的次數爲偶數。現在的問題是:小余想知道包含 mm 個片段的音樂一共有多少種。兩段音樂 aabb 同種當且僅當將 aa 的片段重新排列後可以得到 bb。例如:假設 aa

{{1,2},{2,3}}\{\{1,2\},\{2,3\}\}bb{{3,2},{2,1}}\{\{3,2\},\{2,1\}\},那麼 aabb 就是同種音樂。由於種數很多,你只需要

輸出答案模 100000007100000007(質數)的結果。

輸入輸出格式

輸入格式:

從文件input.txt中讀入數據,輸入文件僅一行,具體是用空格隔開的兩個正整數nnmm,分別表示音階的數量和音樂中的片段數。20%20\%的數據滿足n,m5n,m≤550%50\%的數據滿足n,m3000n,m≤3000100%100\%

的數據滿足n,m1000000n,m≤1000000

輸出格式:

輸出文件 output.txt 僅包含一個非負整數,表示音樂的種數模 100000007100000007 的結果。【輸入輸出樣例】

輸入輸出樣例

輸入樣例#1:

2 3

輸出樣例#1:

1

解題分析

題目無非是讓我們在選子集的時候滿足三個條件:

  1. 選的每個元素都出現偶數次
  2. 選的子集不爲空
  3. 不能選重複的子集

假設我們正在考慮計算dp[i]dp[i]。如果只要求滿足第一個要求, 直接確定i1i-1個之前選的子集, 第ii個子集就唯一確定了, 所以這裏算出來的方案數是(2n1i1)×(i1)\binom{2^n-1}{i-1}\times (i-1)

然後考慮爲空的限制, 發現當且僅當前i1i-1個本身就滿足條件纔會導致第ii個爲空, 所以減去dp[i1]dp[i-1]即可。

最後一個限制, 發現只可能是第ii個和前i1i-1箇中的一個重複了, 那麼剩下的i2i-2個就是合法的了, 也就是dp[i2]dp[i-2]。 重複的一個的選法有(2n1)(i2)(2^n-1)-(i-2), 然後重複的那個子集在i1i-1個子集中有i1i-1個不同的位置, 因此這一部分減去的貢獻是dp[i2]×(i1)×(2n1(i2))dp[i-2]\times (i-1)\times (2^n-1-(i-2))

代碼如下:

#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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章