版权声明:欢迎神犇指教 https://blog.csdn.net/sdxtcqs/article/details/86617177
https://zhixincode.com/contest/16/problem/G?problem_id=243
题意:求
的排列中满足对于任意
,若
为奇数
,若
为偶数
,
。
这题比赛的时候暴力出了前10项就oeis了,然后查到一篇关于齿排列的论文https://max.book118.com/html/2015/0907/24832737.shtm直接找到了递推公式如下
然后就直接写上去交了,要注意除以
的时候要用乘法逆元,因为这个WA了一发。
讲题的时候才知道正解应该是DP,DP想起来也不是很难,令
表示前
个数以
结尾的符合题目条件的
的排列。
当
是奇数时(山峰),要求前面的数比
小,可得
;
当
是偶数时(山谷),要求前面的数比
大,然而之前并没有出现
这个数字,但是有一个巧妙的方法就是可以将之前出现过的大于等于
的数字全部加
,即可符合条件,所以可得
;
这显然是个
的做法,使用前缀和可以优化到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;
}