https://zhixincode.com/contest/16/problem/G?problem_id=243
题意:求
1..
n
1..n
1..n的排列中满足对于任意
i
>
1
i>1
i>1,若
i
i
i为奇数
a
i
−
1
<
a
i
a_{i-1}<a_i
ai−1<ai,若
i
i
i为偶数
a
i
−
1
>
a
i
a_{i-1}>a_i
ai−1>ai,
n
<
=
1000
n<=1000
n<=1000。
这题比赛的时候暴力出了前10项就oeis了,然后查到一篇关于齿排列的论文https://max.book118.com/html/2015/0907/24832737.shtm直接找到了递推公式如下
A
0
=
A
1
=
1
A_0=A_1=1
A0=A1=1
2
A
n
+
1
=
∑
k
=
0
n
(
n
k
)
A
k
A
n
−
k
(
n
≥
1
)
2A_{n+1}=\sum^{n}_{k=0}\dbinom{n}{k}A_kA_{n-k}\ (n\ge1)
2An+1=k=0∑n(kn)AkAn−k (n≥1)
然后就直接写上去交了,要注意除以
2
2
2的时候要用乘法逆元,因为这个WA了一发。
讲题的时候才知道正解应该是DP,DP想起来也不是很难,令
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示前
i
i
i个数以
j
j
j结尾的符合题目条件的
1..
i
1..i
1..i的排列。
当
i
i
i是奇数时(山峰),要求前面的数比
j
j
j小,可得
f
[
i
]
[
j
]
=
∑
k
=
1
j
−
1
f
[
i
−
1
]
[
k
]
f[i][j]=\sum^{j-1}_{k=1}{f[i-1][k]}
f[i][j]=∑k=1j−1f[i−1][k];
当
i
i
i是偶数时(山谷),要求前面的数比
j
j
j大,然而之前并没有出现
i
i
i这个数字,但是有一个巧妙的方法就是可以将之前出现过的大于等于
j
j
j的数字全部加
1
1
1,即可符合条件,所以可得
f
[
i
]
[
j
]
=
∑
k
=
j
i
−
1
f
[
i
−
1
]
[
k
]
f[i][j]=\sum^{i-1}_{k=j}{f[i-1][k]}
f[i][j]=∑k=ji−1f[i−1][k];
这显然是个
O
(
n
3
)
O(n^3)
O(n3)的做法,使用前缀和可以优化到O(n^2)。
//直接套用齿排列递推公式
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int MOD=1000000007;
int n;
long long A[1010];
long long C[1010][1010];
int main()
{
scanf("%d",&n);
A[0]=A[1]=A[2]=1;
C[1][0]=C[1][1]=1;
for (int i=2;i<=1000;i++)
{
C[i][0]=1;
for(int j=1;j<=1000;j++)
{
C[i][j]=(C[i-1][j]+C[i-1][j-1]);
C[i][j]%=MOD;
}
}
for(int i=3;i<=1000;i++)
{
for(int j=0;j<=i-1;j++)
{
A[i]+=(C[i-1][j]*A[j]%MOD)*A[i-1-j] % MOD;
A[i]%=MOD;
}
A[i]=(A[i] * 500000004)%MOD;
}
printf("%lld\n",A[n]);
return 0;
}
//DP做法
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
const int MOD=1000000007;
int f[1010][1010],s[1010][1010];
int main()
{
int n;
scanf("%d",&n);
f[1][1]=s[1][1]=1;
for(int i=2;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
if(i%2==1)
{
f[i][j]=s[i-1][j-1]%MOD;
s[i][j]=(s[i][j-1]+f[i][j])%MOD;
}
if(i%2==0)
{
f[i][j]=(s[i-1][i-1]-s[i-1][j-1]+MOD)%MOD;
s[i][j]=(s[i][j-1]+f[i][j])%MOD;
}
}
}
printf("%d\n",s[n][n]);
return 0;
}