day24T3改错记
题目描述
\(3 \le n \le 500, P \le 1e9\)
解析
来自了邻桌硕佬的解法,感觉比题解好理解
可以被拆成\(3\)个上升子序列等价于最长下降子序列不超过\(3\)
依次考虑每个位置放哪个数,结合\(O(n \log n)\)求最长下降子序列的方法
设\(dp(i, j, k)\)表示还剩下\(i\)个数要放,其中有\(j\)个数比长度为\(1\)的下降子序列的末尾最大元素(设为\(a\))大,\(k\)个比长度为\(2\)的下降子序列的末尾最大元素(设为\(b\))大
现在要放的数\(x\)分为三个部分:
- \(x > a\):
- 放了\(x\)之后\(a\)会被更新
- \(b < x < a\):
- 放了\(x\)之后\(b\)会被更新
- \(x < b\) :
- 如果放了\(x\),最长下降子序列长度变为\(3\),若\(x\)不是最小,之后不论怎么放都不满足条件
- 所以\(x\)只能是最小的那个
- 特判不存在这种情况的状态
那么就会有:
\[dp(i, j, k) = \sum_{h = 0}^{j - 1} dp(i- 1, h, k - 1) + \sum_{h = j}^{k - 1}dp(i - 1, j, h) + [k \neq i]dp(i - 1, j, k)
\]
两个\(\sum\)都可以前缀和优化,然后就可以\(O(n^3)\)啦!!开心一交,为什么\(T\)了??!!
嗯……计算次数那么多,每次调用全局变量多慢啊……干嘛不在三层\(for\)里面先拿个局部变量存一下当前答案呢……
还有加法就不要取模了……判一下大了就减吧……
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#define MAXN 505
typedef long long LL;
int f[2][MAXN][MAXN], sum1[2][MAXN][MAXN], sum2[2][MAXN][MAXN], N, P;
inline int add(int x, int y) { x += y; return x >= P ? x - P : x; }
inline int sub(int x, int y) { x -= y; return x < 0 ? x + P : x; }
inline void inc(int &x, int y) { x += y; if (x >= P) x -= P; }
inline void dec(int &x, int y) { x -= y; if (x < 0) x += P; }
int main() {
freopen("yuan.in", "r", stdin);
freopen("yuan.out", "w", stdout);
scanf("%d%d", &N, &P);
for (int i = 1; i <= N + 1; ++i){
for (int j = 1; j <= N + 1; ++j)
for (int k = j; k <= N + 1; ++k) {
int tmp;
if (i == 1 || k == 1) tmp = 1;
else {
tmp = sum1[(i ^ 1) & 1][j - 1][k - 1];
inc(tmp, sub(sum2[(i ^ 1) & 1][j][k - 1], sum2[(i ^ 1) & 1][j][j - 1]));
if (k ^ i) inc(tmp, f[(i ^ 1) & 1][j][k]);
}
f[i & 1][j][k] = tmp;
sum1[i & 1][j][k] = add(sum1[i & 1][j - 1][k], tmp);
sum2[i & 1][j][k] = add(sum2[i & 1][j][k - 1], tmp);
}
}
printf("%d\n", f[(N ^ 1) & 1][N + 1][N + 1]);
return 0;
}
//Rhein_E 100pts