洛谷传送门
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);
}