题目描述
众所周知卡农是一种复调音乐的写作技法,小余在听卡农音乐时灵感大发,发明了一种新的音乐谱写规则。他将声音分成 n 个音阶,并将音乐分成若干个片段。音乐的每个片段都是由 1 到 n 个音阶构成的和声,即从 n 个音阶中挑选若干个音阶同时演奏出来。为了强调与卡农的不同,他规定任意两个片段所包含的音阶集合都不同。同时为了保持音乐的规律性,他还规定在一段音乐中每个音阶被奏响的次数为偶数。现在的问题是:小余想知道包含 m 个片段的音乐一共有多少种。两段音乐 a 和 b 同种当且仅当将 a 的片段重新排列后可以得到 b。例如:假设 a
为{{1,2},{2,3}},b 为{{3,2},{2,1}},那么 a 与 b 就是同种音乐。由于种数很多,你只需要
输出答案模 100000007(质数)的结果。
题解
这道题我们可以把它看做是从(2^n-1)个子集中选出(m)个子集满足:
1、这些子集所有的元素的出现次数为偶数。
2、任意两个子集不相同。
3、任意子集非空。
至于顺序的问题,我们可以先统计出所有排列的方法,最后除以(m!)即可。
考虑设(f_i)表示(i)个子集的方案数。
我们先考虑满足第一个条件。
如果我们已经钦定了(i-1)个集合,那么最后一个集合也自然可以确定。
所以不考虑最后一个是否为空或者和之前的某个一样的情况的话,答案就是(A_{2^n-1}^{i-1})。
如果第(i)个子集空了,说明前面的偶数次这个条件就已经和法了,所以我们只需要减去(f_{i-1})即可。
再考虑第i个子集和前面的子集重复的情况,同时这两个集合满足了偶数限制,去掉它之后同样满足。
所以方案数为(f_{i-1}),这个集合可能有((2^n-1-(i-2)))种选择,前面那个集合有((i-1))种放法。
代码
#include<bits/stdc++.h>
#define N 1000009
using namespace std;
typedef long long ll;
const int mod=1e8+7;
ll p[N],jie[N],dp[N],n,m;
inline ll rd(){
ll x=0;char c=getchar();bool f=0;
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f?-x:x;
}
inline ll power(ll x,ll y){
ll ans=1;
while(y){
if(y&1)ans=ans*x%mod;x=x*x%mod;y>>=1;
}
return ans;
}
int main(){
n=rd();m=rd();
ll ci=power(2,n);
jie[0]=1;
for(int i=1;i<=m;++i)jie[i]=jie[i-1]*i%mod;
ll now=1,cii=ci;
for(int i=1;i<=m;++i){
p[i]=now;
cii--;cii=(cii+mod)%mod;
now=now*cii%mod;
}
dp[0]=1;dp[1]=0;
for(int i=2;i<=m;++i){
dp[i]=p[i]-dp[i-1];
dp[i]-=dp[i-2]*(i-1)%mod*(ci-i+1)%mod;
dp[i]=(dp[i]%mod+mod)%mod;
}
cout<<dp[m]*power(jie[m],mod-2)%mod;
return 0;
}