[HNOI2011]卡农

题目描述

众所周知卡农是一种复调音乐的写作技法,小余在听卡农音乐时灵感大发,发明了一种新的音乐谱写规则。他将声音分成 n 个音阶,并将音乐分成若干个片段。音乐的每个片段都是由 1 到 n 个音阶构成的和声,即从 n 个音阶中挑选若干个音阶同时演奏出来。为了强调与卡农的不同,他规定任意两个片段所包含的音阶集合都不同。同时为了保持音乐的规律性,他还规定在一段音乐中每个音阶被奏响的次数为偶数。现在的问题是:小余想知道包含 m 个片段的音乐一共有多少种。两段音乐 a 和 b 同种当且仅当将 a 的片段重新排列后可以得到 b。例如:假设 a

为{{1,2},{2,3}},b 为{{3,2},{2,1}},那么 a 与 b 就是同种音乐。由于种数很多,你只需要

输出答案模 100000007(质数)的结果。

输入输出格式

输入格式:

从文件input.txt中读入数据,输入文件仅一行,具体是用空格隔开的两个正整数n和m,分别表示音阶的数量和音乐中的片段数。20%的数据满足n,m≤5,50%的数据满足n,m≤3000,100%

的数据满足n,m≤1000000。

输出格式:

输出文件 output.txt 仅包含一个非负整数,表示音乐的种数模 100000007 的结果。【输入输出样例】

输入输出样例

输入样例#1:

2 3

输出样例#1:

1

说明

样例解释:音乐为{{1},{2},{1,2}}


题解

计数DP+容斥
反正计数题我肯定只能抄题解
首先发现题目的要求有两个:

1.每段不能相同
2.每个音阶的总出现次数一定是偶数

首先题目要求无序的集合
处理无序集合很麻烦,可以直接按照有序集合处理最后再除以(m!)
可以发现这个每个音阶出现总次数是偶数的限制考虑起来很麻烦
这种情况下直接递推似乎很不可行
可以考虑一个容斥的(DP)
(f_i)表示选择(i)段并且使这(i)段已经合法的方案数
那么这样我们怎么处理偶数的限制呢?
考虑到了第(m)
那么我们已经选好了前(m-1)段,那么对于第(m)段我们能选择的东西其实就已经确定了,就是那些出现次数为奇数的音符
所以我们也可以这样来考虑求(f_i)
首先选择(i)段的总方案数为(A_{i}^{2^n-1})
但是注意我们实际上对于(f_i)是要确定前(i-1)段选啥,因为确定了前(i-1)段选啥第(i)段就已经确定了,所以实际上我们要考虑的是(A_{i-1}^{2^n-1})
然后考虑去除不合法情况:

1.第(i)段是空集的方案数:(f_{i-1})
2.第(i)段和前面选择的某一段重复了:((i-1) imes f_{i-2} imes (2^n-1-(i-2)))

所以递推式就是(f_i=A_{i-1}^{2^n-1}-f_{i-1}-(i-1) imes f_{i-2} imes (2^n-1-(i-2)))

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 1000005 ;
const int mod = 1e8 + 7 ;
using namespace std ;

int n , m , pw2 , fac_m , A[M] , f[M] ;
inline int Fpw(int Base , int k) {
	int temp = 1 ;
	while(k) {
		if(k & 1) temp = 1LL * temp * Base % mod ;
		Base = 1LL * Base * Base % mod ; k >>= 1 ;
	}
	return temp ;
}
int main() {
	scanf("%d%d",&n,&m) ; 
	pw2 = Fpw(2 , n) - 1 ; A[0] = 1 ; fac_m = 1 ;
	for(int i = 1 ; i <= m ; i ++) {
		A[i] = 1LL * A[i - 1] * (pw2 - i + 1) % mod ;
		fac_m = 1LL * fac_m * i % mod ;
	}
	f[0] = 1 ; f[1] = 0 ;
	for(int i = 2 ; i <= m ; i ++) {
		f[i] = ( A[i - 1] - f[i - 1] ) % mod - 1LL * (i - 1) * f[i - 2] % mod * (pw2 - (i - 2)) % mod ;
		f[i] = (f[i] % mod + mod) % mod ;
	}
	printf("%lld
",1LL * f[m] * Fpw(fac_m , mod - 2) % mod) ;
	return 0 ;
}
原文地址:https://www.cnblogs.com/beretty/p/10772509.html