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