以数字个数 i i i作为阶段,表示 1 1 1~ i i i的排列
设 f [ i ] [ j ] [ 0 / 1 ] f[i][j][0/1] f[i][j][0/1]表示 1 1 1到 i i i的排列以 j j j结尾,第三维为 0 0 0表示峰 为 1 1 1表示谷
则容易写出一个方程
f
[
i
]
[
j
]
[
0
]
=
∑
k
=
1
j
−
1
f
[
i
−
1
]
[
k
]
[
1
]
f[i][j][0] = \sum_{k = 1}^{j-1}f[i-1][k][1]
f[i][j][0]=k=1∑j−1f[i−1][k][1]
以及
f
[
i
]
[
j
]
[
1
]
=
∑
k
=
j
+
1
i
f
[
i
−
1
]
[
k
]
[
0
]
f[i][j][1] = \sum_{k = j + 1}^{i}f[i-1][k][0]
f[i][j][1]=k=j+1∑if[i−1][k][0]
注意到第二个方程从上一个阶段转移过来时,第 i − 1 i-1 i−1个阶段中没有 f [ i − 1 ] [ i ] [ 0 ] f[i-1][i][0] f[i−1][i][0]这个状态
但是在第 i i i个阶段时, i i i是可以放在前 i − 1 i-1 i−1个元素中的 上面的方程无法统计这个状况
处理方法如下:
注意到i比其他元素都要大,所以i可以代替任何一个其他的峰 方案数同该数作为峰时的方案数
所以我们可以在答案中加上 f [ i − 1 ] [ j ] [ 0 ] f[i-1][j][0] f[i−1][j][0]表示把 i i i放在位置 i − 1 i-1 i−1作为峰的方案数 这样计数是不重不漏的
所以 第二个方程变为 f [ i ] [ j ] [ 1 ] = ∑ k = j i f [ i − 1 ] [ k ] [ 0 ] f[i][j][1] = \sum_{k = j}^{i} f[i-1][k][0] f[i][j][1]=k=j∑if[i−1][k][0]
这样转移时间复杂度 O ( n 3 ) O(n^3) O(n3) 空间复杂度 O ( n 2 ) O(n^2) O(n2) 都不太能接受
解决方法与
N
O
I
P
2015
NOIP2015
NOIP2015子串一题类似
观察到
f
[
i
]
[
j
]
[
0
/
1
]
f[i][j][0/1]
f[i][j][0/1]与
f
[
i
]
[
j
+
1
]
[
0
/
1
]
f[i][j+1][0/1]
f[i][j+1][0/1]的方程有重叠,我们可以直接从同阶段与
j
j
j相邻的状态转移,并用滚动数组优化
最终
f
[
i
]
[
j
]
[
0
]
=
f
[
i
]
[
j
−
1
]
[
0
]
+
f
[
i
−
1
]
[
j
−
1
]
[
1
]
f[i][j][0]=f[i][j-1][0]+f[i-1][j-1][1]
f[i][j][0]=f[i][j−1][0]+f[i−1][j−1][1]
f
[
i
]
[
j
]
[
1
]
=
f
[
i
]
[
j
+
1
]
[
1
]
+
f
[
i
]
[
j
]
[
0
]
f[i][j][1]=f[i][j+1][1]+f[i][j][0]
f[i][j][1]=f[i][j+1][1]+f[i][j][0]
使用滚动数组后
时间复杂度
O
(
n
2
)
O(n^2)
O(n2) 空间复杂度
O
(
n
)
O(n)
O(n)
代码如下
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define Max(_A,_B) (_A>_B?_A:_B)
#define Min(_A,_B) (_A<_B?_A:_B)
#define maxn 4207
#define maxm 1000001
#define V e[i].v
#define ull unsigned long long
int n, m, k, tot;
inline int read(){
int s = 0, f = 1; char c = getchar();
while (c > '9' || c < '0') {if(c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') {s = s * 10 + c - '0'; c = getchar();}
return s * f;
}
int f[2][maxn][2];
int ans, p;
int main(){
n = read(), p = read();
//p = 1e9+7;
f[1][1][0] = f[1][1][1] = 1;
for (int i = 2; i <= n; i++) {
for (int j = 1; j <= i; j++) {
f[i&1][j][0] = 0;
/*for (int k = 1; k < j; k++) {
f[i&1][j][0] += f[(i - 1)&1][k][1];
f[i&1][j][0] %= p;
}*/
f[i&1][j][0] = f[i&1][j-1][0] + f[(i-1)&1][j-1][1];
f[i&1][j][0] %= p;
}
for (int j = i; j >= 1; j--) {
f[i&1][j][1] = 0;
f[i&1][j][1] = f[i&1][j+1][1] + f[(i-1)&1][j][0];
f[i&1][j][1] %= p;
}
}
for (int i = 1; i <= n; i++) {
ans += f[n&1][i][0] + f[n&1][i][1];
ans %= p;
}
printf("%d\n", ans);
return 0;
}