洛谷传送门
BZOJ传送门
题目描述
众所周知卡农是一种复调音乐的写作技法,小余在听卡农音乐时灵感大发,发明了一种新的音乐谱写规则。他将声音分成 n n n 个音阶,并将音乐分成若干个片段。音乐的每个片段都是由 1 1 1 到 n n n 个音阶构成的和声,即从 n n n 个音阶中挑选若干个音阶同时演奏出来。为了强调与卡农的不同,他规定任意两个片段所包含的音阶集合都不同。同时为了保持音乐的规律性,他还规定在一段音乐中每个音阶被奏响的次数为偶数。现在的问题是:小余想知道包含 m m m 个片段的音乐一共有多少种。两段音乐 a a a 和 b b b 同种当且仅当将 a a a 的片段重新排列后可以得到 b b b。例如:假设 a a a
为 { { 1 , 2 } , { 2 , 3 } } \{\{1,2\},\{2,3\}\} {{1,2},{2,3}}, b b b 为 { { 3 , 2 } , { 2 , 1 } } \{\{3,2\},\{2,1\}\} {{3,2},{2,1}},那么 a a a 与 b b b 就是同种音乐。由于种数很多,你只需要
输出答案模 100000007 100000007 100000007(质数)的结果。
输入输出格式
输入格式:
从文件input.txt中读入数据,输入文件仅一行,具体是用空格隔开的两个正整数 n n n和 m m m,分别表示音阶的数量和音乐中的片段数。 20 % 20\% 20%的数据满足 n , m ≤ 5 n,m≤5 n,m≤5, 50 % 50\% 50%的数据满足 n , m ≤ 3000 n,m≤3000 n,m≤3000, 100 % 100\% 100%
的数据满足 n , m ≤ 1000000 n,m≤1000000 n,m≤1000000。
输出格式:
输出文件 output.txt 仅包含一个非负整数,表示音乐的种数模 100000007 100000007 100000007 的结果。【输入输出样例】
输入输出样例
输入样例#1:
2 3
输出样例#1:
1
解题分析
题目无非是让我们在选子集的时候满足三个条件:
- 选的每个元素都出现偶数次
- 选的子集不为空
- 不能选重复的子集
假设我们正在考虑计算 d p [ i ] dp[i] dp[i]。如果只要求满足第一个要求, 直接确定 i − 1 i-1 i−1个之前选的子集, 第 i i i个子集就唯一确定了, 所以这里算出来的方案数是 ( 2 n − 1 i − 1 ) × ( i − 1 ) \binom{2^n-1}{i-1}\times (i-1) (i−12n−1)×(i−1)。
然后考虑为空的限制, 发现当且仅当前 i − 1 i-1 i−1个本身就满足条件才会导致第 i i i个为空, 所以减去 d p [ i − 1 ] dp[i-1] dp[i−1]即可。
最后一个限制, 发现只可能是第 i i i个和前 i − 1 i-1 i−1个中的一个重复了, 那么剩下的 i − 2 i-2 i−2个就是合法的了, 也就是 d p [ i − 2 ] dp[i-2] dp[i−2]。 重复的一个的选法有 ( 2 n − 1 ) − ( i − 2 ) (2^n-1)-(i-2) (2n−1)−(i−2), 然后重复的那个子集在 i − 1 i-1 i−1个子集中有 i − 1 i-1 i−1个不同的位置, 因此这一部分减去的贡献是 d p [ i − 2 ] × ( i − 1 ) × ( 2 n − 1 − ( i − 2 ) ) dp[i-2]\times (i-1)\times (2^n-1-(i-2)) dp[i−2]×(i−1)×(2n−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);
}