\(f_i\) 表示以第 \(i\) 个结尾,强制选第 \(i\) 个所能得到的最长几乎上升序列的长度。

\(f_i=\max\limits_{j\lt i,a_j\le a_i}\left\{f_j+1+w(i,j)\right\}\)

其中 \(w(i,j)=0/1\) 表示 \([j+1,i-1]\) 有没有比 \(a_i\)\(a_j\) 都大的数。

对于每个 \(i\) 用单调栈预处理出 \(pre_i\) 表示 \(i\) 左边第一个大于 \(a_i\) 的位置,没有则为 \(0\)

\(f_i=\max(\max\limits_{j\lt pre_i,a_j\le a_i}\left\{f_j\right\}+2,\max\limits_{pre_i\le j\lt i,a_j\le a_i}\left\{f_j\right\}+1)\)

\(a\) 从小到大排序后依次加入,发现涉及到的操作有求前缀 \(\max\),单点修改,用树状数组维护即可。

具体细节看代码。

Code:

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef pair <int, int> pii;
const int N = 500005;
int T;
int n;
pii a[N]; int stk[N], top;
int f[N], pre[N];
int c[N];

void upd(int x, int y) { for (; x <= n; x += x & -x) c[x] = max(c[x], y); }
int query(int x) { int res = 0; for (; x; x -= x & -x) res = max(res, c[x]); return res; }

void solve() {
	scanf("%d", &n);
	memset(c + 1, 0, sizeof (int) * n);
	for (int i = 1; i <= n; ++i) scanf("%d", &a[i].fi), a[i].se = i;
	top = 0;
	for (int i = 1; i <= n; ++i) {
		while (top && a[stk[top]] <= a[i]) --top;
		pre[i] = stk[top], stk[++top] = i;
	}
	sort(a + 1, a + n + 1);
	for (int i = 1; i <= n; ++i) {
		int _ = a[i].se;
		f[_] = query(_ - 1) + 1;
		if (pre[_]) f[_] = max(f[_], query(pre[_] - 1) + 2);
		upd(_, f[_]);
	}
	printf("%d\n", query(n));
}

int main() {
	scanf("%d", &T);
	while (T--) solve();
	return 0;
}

原文地址:http://www.cnblogs.com/Kobe303/p/16798814.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性