ssoj1306卡农(canon)(组合数学+乘法逆元)

来源:互联网 发布:ue ui设计美工招聘 编辑:程序博客网 时间:2024/04/29 18:41

题目描述

众所周知卡农是一种复调音乐的写作技法,小余在听卡农音乐时灵感大发,发明了一种新的音乐谱写规则。他将声音分成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的结果。

样例输入

2 3

样例输出

1

提示

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




<span style="font-size:18px;">#include <iostream>#include <cstring>#include <cstdlib>#include <cstdio>#include <algorithm>#include <cmath>#define ll long long using namespace std;const int maxn=1000006;const int mod=100000007;int n,m;ll f[maxn],g[maxn],Pow,d,x,y;inline int get(){    char c;while(!isdigit(c=getchar()));    int v=c-48;while(isdigit(c=getchar()))v=v*10+c-48;    return v;}inline ll exgcd(ll a,ll b,ll &x,ll &y){    if(b==0){    x=1;y=0;return a;}else{    ll tmp=exgcd(b,a%b,x,y);    ll t=x;    x=y;    y=t-(a/b)*y;    return tmp;}}inline ll power(int a,int b){    ll ret=1,tmp=a;    while(b){    if(b&1)ret=ret*tmp%mod;    tmp=tmp*tmp%mod;    b>>=1;}return ret;}int main(){    n=get();m=get();    Pow=power(2,n)%mod;      g[0]=1;f[0]=f[1]=f[2]=0;    for(int i=1;i<=m;++i)g[i]=g[i-1]*(Pow-i)%mod;    ll i;    for(i=3;i<=m;++i){    f[i]=g[i-1]-f[i-1]-(i-1)*(Pow+1-i)%mod*f[i-2]%mod;    f[i]%=mod;}if(f[m]<0)f[m]+=mod;d=1;for(int i=1;i<=m;++i)d=d*i%mod;exgcd(d,mod,x,y);d=(x%mod+mod)%mod;f[m]=d*f[m]%mod;printf("%lld\n",f[m]);return 0;}</span>


思路:由数学知识可知总共有2^n-1个集合,f[ i ]为取i个集合的合法方案,g[ i ]为取i个集合的总方案(包括不合法的),因为音阶要取偶数个,所以第m个方案可由前m-1的方案决定。即f[ i ]为g[ i-1 ]-♂(某个数)。原来是无序,现在我们假设它有序,最后再用乘法逆元求(f[ m ]/m!)mod MOD。

这♂是什么呢

        “不合法的那些方案,不合法的方案有两种: 
1、前i1段的方案是合法的,这时前i1段,每种音调的个数是偶数个,而加入第i段后,由于第i段中每种不同的音调都只能出现1次,那么加入第i段后每种音调的个数可能出现奇数个,就不合法了,这种情况的方案数有f[i1] 
2、第i段和前i1段中某一段是相同的,为了便于叙述,我们说前i1段中与第i段相同的段是第i段的冲突对象,那么冲突对象可以在1到i1号段中选一个,有(i1)种选法,选好冲突对象后,剩下的i2个不冲突的段的方案数是f[i2](有2个子集被去掉了,某些种音调里去掉了偶数次,现在每种音调的出现次数就是偶数次,那么剩下的段肯定是合法的),第i段和其冲突对象对应的子集有(2n1)(i2),解释:n个音调的子集总数共有(2n1)种,然后前i1段中有i2个不和第i段相同的段已经确定下来了,占用了其中的i2个子集,因此前面的式子是(2n1)(i2),因此乘法计数可以得到二号情况的方案数是f[i2](i1)[(2n1)(i2)] 
那么就能得到初步的一个递推式: 
f[i]=Ai12n1f[i1]f[i2](i1)[(2n1)(i2)] 
然后用g[i]代进去替换掉那个A 
f[i]=g[i1]f[i1]f[i2](i1)[(2n1)(i2)]


0 0