51nod 1556 计算
有一个1*n的矩阵 固定第一个数为1 其他填正整数
且相邻数的差不能超过1 求方案数%(109+7)的结果
1*n的矩阵。前后的绝对值差不超过1.
设 第i个数大小为
Ai
则有
Ai−Ai−1=0 , 1 ,−1
那么。前后项做差,我们会得到一个序列。
这个序列仅仅由1,0,-1组成
(组合数学里面讲卡特兰数时是我第一次见+1,-1,组成序列的许多性质)
原问题等价于1,-1,0,组成的序列,任意前缀和不小于1的序列数量。
设 有 n个 +1 ,m个 −1组成的序列{ai}
其中n>=m
则一个合法序列为 :任意前缀和不小于0的序列。
即:
∑i=1kai≥0,其中 k≤n+m
则合法序列的数量为:
(n+mm)−(n+mm−1)
上面的公式可以用于计算出来第n个卡特兰数
————————不喜欢看计算的可以跳过——————————–
不特别说明的话 ,下面都有
n>=m
细心观察,上面公式左边:
(n+mm)
相当于,所有排列数量。(多重集合的排列。。。
其实 。右边:
(n+mm−1)
相当于不合法序列的数量;
应用减法原理。便有这个公式。
对于第一部分。 n个+1与m个-1 组成的序列的所有排列情况数
我们只需要确定其中+1或者-1的位置,而另一种数字的位置也就随之确定了。
我们先让-1选择属于它的一些位置。-1选择位置 的 方案数量为:
(n+mm)
公式的第二部分,不合法序列的数量:
为了便于理解。
我们来发掘一些不合法序列必有的特征:
首先。既然序列不合法。那么必然存在在某个位置 k,有:
∑i=1kai<0
最小的那个 k 很重要
虽然不能描述整个序列的情况。
但它概述了一类这样不合法的序列。
即:在k位置第一次出现前缀和小于0(特征1部分,即前k个数)
注意:是第一次。
那么对于序列
a1,a2,....,ak
这部分很特别。 我们说是特征。
通过把这k个数取反。我们得到了一个由
n+1个+1m−1个−1
组成的序列。对于这个序列,有:
k是第一次出现前缀和大于0的位置。(特征2部分,即前k个数)
这也就是说。n+1个+1,m-1个-1组成的序列,都可以通过取反特征2部分。
来变换出一个由 n个+1,m个-1组成的不合法序列。
同时 n个+1,m个-1组成的序列,也可以通过取反特征1部分。
来得到一个由n+1个+1,m-1个-1组成的序列。
综上,n个+1,m个-1组成的不合法序列的数量为,即为,n+1个+1,m-1个-1组成序列的数量:(n+mm−1)
所以:n个+1,m个-1,n>=m组成任意前缀和不小于0的序列数量为:
(n+mm)−(n+mm−1)
———————————–计算结束————————————————
回到原问题:
对于 长度为 x的序列,只使用 ±1 ,组成任意前缀和不小于0
的序列数量为 记为:S[x]
则有:
S[x]=∑i=0⌊x2⌋[(xi)−(xi−1)]=(x⌊x2⌋)
因为第一个元素为1,在不考虑第一个元素,原问题等价于,任意前缀和不小于0.
序列的基本元素除了x个 ±1 之外,还有 0.
所以,枚举不同的x并添加 (n−1−x) 个0,来得到答案。
这里之所以是n-1-x个0,是因为第一个位置固定为1,序列长度变为n-1,原问题变形为前缀和不小与0
综上有:answer=∑x=0n−1(S[x]∗(n−1n−1−x))=∑x=0n−1((x⌊x2⌋)∗(n−1x))
其中:(00)=1
下面是代码:
#include <algorithm>
#include <string.h>
#include <stdio.h>
#define MAXN 1000005
using namespace std;
typedef long long LL;
const LL P=1e9+7;
LL Ivn[MAXN]={0,1};
LL S[MAXN];
int main ()
{
int n,sz=1;
scanf("%d",&n);
if(n==1)
{
printf("1\n");
return 0;
}
sz+=n;
for(int i=2;i<sz;i++) Ivn[i]=P-P/i*Ivn[P%i]%P;
S[0]=1;
S[1]=1;
S[2]=2;
for(int i=3,j=4; i<sz;i+=2,j+=2)
{
S[i]=S[i-2];
S[i]*=i; S[i]%=P;
S[i]*=i-1; S[i]%=P;
S[i]*=Ivn[((i-2)>>1)+1]; S[i]%=P;
S[i]*=Ivn[((i-2)>>1)+2]; S[i]%=P;
S[j]=S[j-2];
S[j]*=j-1; S[j]%=P;
S[j]*=j; S[j]%=P;
S[j]*=Ivn[((j-2)>>1)+1]; S[j]%=P;
S[j]*=Ivn[((j-2)>>1)+1]; S[j]%=P;
}
LL ans=0,C=1;
for(int i=0;i<n;i++)
{
ans+=C*S[i]%P;
if(ans>=P)ans-=P;
C*=n-i-1;
C%=P;
C*=Ivn[i+1];
C%=P;
}
printf("%lld\n",ans);
return 0;
}