双端队列×LIS问题 \operatorname{双端队列×LIS问题} 双端队列×LIS问题
题目链接: S S L 比 赛 1480 SSL比赛\ 1480 SSL比赛 1480
题目
输入
输出
样例输入
我来让你复制:
8
1 9 2 6 8 1 7 1926817
样例输出
我来让你复制:
5
样例解释
数据范围
思路
这道题是一道最长上升 & \& & 下降子序列。
我们可以发现,我们把从前面插入的数在原来的序列找出来,可以发现它包含了最长下降子序列。同理,我们把从后面插入的数在原来的序列找出来,可以发现它包含了最长上升子序列。
再一看,我们可以发现这两个序列的数合起来,看他们在答案序列的位置,就发现包含这个序列的最长上升子序列。
但是我们发现会有重复的地方,但是又因为数不相等,就有且只有一个重复的。
那我们就用 O ( n l o g n ) O(nlogn) O(nlogn) 的方法求出最长上升子序列和最长下降子序列,我们就枚举重合的那个数,找到包含这个数的最长上升子序列和最长下降子序列,找到最大的那个。最后减一输出就可以了。
提醒:要用快读吧。。。
(好像)
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n, a[100001], b[100001], c[100001];
int ans1[100001], ans2[100001], ans;
int read() {//快读
int re = 0, zhengfu = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') zhengfu = -zhengfu;
c = getchar();
}
while (c >= '0' && c <= '9') {
re = re * 10 + c - '0';
c = getchar();
}
return re * zhengfu;
}
int main() {
n = read();//读入
for (int i = 1; i <= n; i++) {
a[i] = read();//读入
b[n - i + 1] = a[i];//记录
}
for (int i = 1; i <= n; i++) {//最长上升子序列
int now = lower_bound(c + 1, c + c[0] + 1, b[i]) - c;
c[now] = b[i];
if (now > c[0]) c[0]++;
ans1[n - i + 1] = now;
}
for (int i = 1; i <= n; i++)//取反记录
b[i] = -a[n - i + 1];
memset(c, 0, sizeof(c));
for (int i = 1; i <= n; i++) {//最长下降子序列
int now = lower_bound(c + 1, c + c[0] + 1, b[i]) - c;
c[now] = b[i];
if (now > c[0]) c[0]++;
ans2[n - i + 1] = now;
}
ans = 1;
for (int i = 1; i <= n; i++)//找到答案
ans = max(ans, ans1[i] + ans2[i] - 1);
printf("%d", ans);//输出
return 0;
}