题目
问有多少个 1 ∼ n 1\sim n 1∼n的排列对于 ∀ i \forall i ∀i在 1 < i < n 1<i<n 1<i<n的情况下都满足 a [ i − 1 ] < a [ i ] > a [ i + 1 ] a[i-1]<a[i]>a[i+1] a[i−1]<a[i]>a[i+1]或 a [ i − 1 ] > a [ i ] < a [ i + 1 ] a[i-1]>a[i]<a[i+1] a[i−1]>a[i]<a[i+1]
分析
思维好题
首先考虑性质,
- 对于不相邻的两个数 j , j + 1 j,j+1 j,j+1,可以互换
- 合法的排列翻转依旧合法
- 把合法排列每个数 j j j都变成 n − j + 1 n-j+1 n−j+1,排列依旧合法
考虑dp,设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示选择
1
∼
i
1\sim i
1∼i,第一个数为
j
j
j,且第一个数大于第二个数的方案,所以最后答案要乘2
因为
j
j
j和
j
−
1
j-1
j−1在不相邻时可以互换,所以
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]可以由
d
p
[
i
]
[
j
−
1
]
dp[i][j-1]
dp[i][j−1]转移而来,接着如果
j
j
j和
j
−
1
j-1
j−1相邻,那么也就转换成选择
1
∼
i
−
1
1\sim i-1
1∼i−1,第一个数为
j
−
1
j-1
j−1,且第一个数小于第二个数的方案,但是这和定义不符,考虑转换成选择
1
∼
i
−
1
1\sim i-1
1∼i−1,第一个数为
i
−
j
+
1
i-j+1
i−j+1,且第一个数大于第二个数的方案,因为如果进行第三条性质,那么原来大的反而小,初始化
d
p
[
2
]
[
2
]
=
1
dp[2][2]=1
dp[2][2]=1
综上所述,
d
p
[
i
]
[
j
]
=
d
p
[
i
]
[
j
−
1
]
+
d
p
[
i
−
1
]
[
i
−
j
+
1
]
dp[i][j]=dp[i][j-1]+dp[i-1][i-j+1]
dp[i][j]=dp[i][j−1]+dp[i−1][i−j+1]
代码
#include <cstdio>
#define rr register
using namespace std;
int n,mod,dp[2][4211],ans;
inline signed mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
signed main(){
scanf("%d%d",&n,&mod),dp[0][2]=1;
for (rr int i=3;i<=n;++i)
for (rr int j=2;j<=i;++j)
dp[i&1][j]=mo(dp[i&1][j-1],dp[(i&1)^1][i-j+1]);
for (rr int i=2;i<=n;++i) ans=mo(ans,dp[n&1][i]);
return !printf("%d",mo(ans,ans));
}