好神的题:http://blog.csdn.net/lych_cys/article/details/50603389
实际上,任意一种可行的方案的序列都可以转化为本质相同的下述序列:
先是若干个(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)的了。
#include<cstdio> #include<cstdlib> #include<algorithm> #define P 1000000007 using namespace std; typedef long long ll; inline char nc() { static char buf[100000],*p1=buf,*p2=buf; if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; } return *p1++; } inline void read(int &x){ char c=nc(),b=1; for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1; for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b; } const int maxn=2000000; ll fac[maxn+5],inv[maxn+5]; inline void Pre(){ fac[0]=1; for (int i=1;i<=maxn;i++) (fac[i]=fac[i-1]*i)%=P; inv[1]=1; for (int i=2;i<=maxn;i++) (inv[i]=(P-P/i)*inv[P%i])%=P; inv[0]=1; for (int i=1;i<=maxn;i++) (inv[i]*=inv[i-1])%=P; } inline ll C(int n,int m){ return fac[n]*inv[m]%P*inv[n-m]%P; } inline ll F(int n,int m){ if (m<0) return 0; return C(m+n-1,n-1); } int n,m; ll ans; int main() { freopen("t.in","r",stdin); freopen("t.out","w",stdout); Pre(); read(n); read(m); if (!n || !m) return printf("0\n"),0; ans=1; m=min(n,m); for (int i=2;i<=m;i++) { (ans+=F(i,(n-i)>>1))%=P; (ans+=F(i,(n-i-1)>>1))%=P; } printf("%lld\n",ans); return 0; }