题目链接
http://www.lydsy.com/JudgeOnline/problem.php?id=2339
思路
深感自己的数学有多么的弱。。。。完了完了。。。赶快回去补MO去
先暂时修改下题意,排列不同的方案看成不同的方案,比如{{1,2},{3,4}}和{{3,4},{1,2}}是不同的方案,这样用排列数就没有除法的问题。
用
f[i]
来表示前
i
段的合法方案数,
那么我们可以采取补集转换的思想,在推出
f[i]
时,我们可以用前
i−1
段的所有方案数,包括不合法方案,就是一个大小为
n
的集合中选
1、前
i−1
段的方案是合法的,这时前
i−1
段,每种音调的个数是偶数个,而加入第
i
段后,由于第
2、第
i
段和前
那么就能得到初步的一个递推式:
f[i]=Ai−12n−1−f[i−1]−f[i−2](i−1)[(2n−1)−(i−2)]
然后用
g[i]
代进去替换掉那个A
f[i]=g[i−1]−f[i−1]−f[i−2](i−1)[(2n−1)−(i−2)]
恩。。。现在比较好搞了,最终很容易推出
f[n]
,然后因为题目本来是要求排列不同的方案看成相同的方案,那么就除以
m!
就可以了,但是模意义下不能做除法,所以用扩欧求出模xxx意义下
m!
的乘法逆元,乘上
f[n]
就是最终答案。
呼。。。这篇题解恐怕是我写得最长的题解了,真TM蛋疼。。。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#define MAXN 1000100
#define MOD 100000007
using namespace std;
typedef long long int LL;
LL n,m;
LL power[MAXN]; //power[i]=2^i
LL g[MAXN],f[MAXN],ans; //f[i]=前i段合法的方案数,g[i]=A(2^n-1,i)
LL fastPow(LL base,LL pow)
{
LL ans=1;
while(pow)
{
if(pow&1) ans=ans*base%MOD;
base=base*base%MOD;
pow>>=1;
}
return ans;
}
LL extGCD(LL a,LL b,LL &x,LL &y) //ax+by=1
{
if(b==0)
{
x=1;
y=0;
return a;
}
LL tmp=extGCD(b,a%b,x,y);
LL t=x;
x=y;
y=t-(a/b)*y;
return tmp;
}
LL reverse(LL a,LL b) //求模b意义下a的逆元x,ax=1(mod b),b是质数所以可以求逆元(gcd(a,b)=1)
{
LL x,y;
extGCD(a,b,x,y);
x=(x%b+b)%b;
return x;
}
void init()
{
LL i,a=fastPow(2,n)-1;
g[0]=1;
for(i=1;i<=m;i++) g[i]=g[i-1]*(a-i+1)%MOD;
}
int main()
{
scanf("%lld%lld",&n,&m);
init();
LL a=fastPow(2,n)-1,i;
f[1]=f[2]=0;
for(i=3;i<=m;i++)
{
f[i]=g[i-1]-f[i-1]-f[i-2]*(i-1)%MOD*(a-(i-2))%MOD;
f[i]%=MOD;
}
if(f[m]<0) f[m]+=MOD;
a=1;
for(i=1;i<=m;i++) a=a*i%MOD;
a=reverse(a,MOD);
f[m]=f[m]*a%MOD;
printf("%lld\n",f[m]);
return 0;
}