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
posted @ 2019-03-27 19:22  Rhein_E  阅读(116)  评论(0编辑  收藏  举报