这道题目看起来好华丽啊。。我一开始还以为是递推找规律,然而看到了本质不同以后就。。。。
实际上,任意一种可行的方案的序列都可以转化为本质相同的下述序列:
先是若干个(1,2),然后是若干个(3,2),然后是若干个(3,4)……以此类推,当然如果长度为奇数就在最后面加上一个数,举个例子:
{1,2,3,2,3,2,3,2,3,4,5,4,5,4,5,4,5,6,5,6,5,6,7}
可以这么证明,首先对于1,1的后面一定跟着一个2,前面也是1个2,那么就把所有的(1,2)都不断移到最前面,显然这个序列仍然符合要求;然后再把(3,2)移到若干个(1,2)的后面,再把(3,4)移到(3,2)的后面……最后就变成上面所描述的序列了。
这样,如果两个序列,符合上面的描述且至少有一位不同,显然这两个序列是本质不同的。这样就解决了本质不同这个问题;同时,将序列有序化了。
假设序列长度为n,最大值为m,n>=m,那么此时有多少种本质不同的方案呢?首先得到序列{1,2,...,m},接下来就是考虑往序列里面加东西使它的长度到大n。显然如果加了,就只能一对一对加,即(1,2)(3,2)(3,4)(5,4)……这些2个2个往里面加,这样总共可以加[(n-m)/2]次,如果最后多了一个,那也只有一种情况了:加在最后面。由于加入的先后顺序没有影响,所以相当于[(n-m)/2]个球里放在(m-1)个盒子里,允许空盒的方案数。这个用组合计数解决就行了。
(补充:m个相同的球放在n个盒子里,允许空盒的方案数为C(m+n-1,n-1),把这个记为F(m,n))
当n,m一定时,答案为F([(n-m)/2],m-1),但这样是O(MN)的。注意到F(0,m-1)+F(1,m-1)+F(2,m-1)+...+F([(n-m)/2],m-1)=F([(n-m)/2],m),这样就变为O(M+N)的了。
AC代码如下:
<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#define ll long long
#define mod 1000000007
using namespace std;
int n,m,f[2000005],inv[2000005];
int solve(int x,int y){
if (x<0) return 0; x>>=1; y--;
return (ll)f[x+y]*inv[x]%mod*inv[y]%mod;
}
int main(){
scanf("%d%d",&n,&m); int i;
f[0]=f[1]=inv[0]=inv[1]=1;
for (i=2; i<=n; i++){
f[i]=(ll)f[i-1]*i%mod; inv[i]=(ll)inv[mod%i]*(mod-mod/i)%mod;
}
for (i=2; i<=n; i++) inv[i]=(ll)inv[i]*inv[i-1]%mod;
int ans=0; if (m&&n) ans=1; m=min(n,m);
for (i=2; i<=m; i++){
ans=((ans+solve(n-i,i))%mod+solve(n-i-1,i))%mod;
}
printf("%d\n",ans);
return 0;
}
</span>
2016.1.30