题目大意:
长度为 n ( ≤ 400 000 ) n(\le 400\,000) n(≤400000)的序列, m ( ≤ 400 000 ) m(\le 400\,000) m(≤400000)次互相无关的替换,每次会替换某一个元素,求每次替换之后的最长上升子序列长度
解题思路:
-
首先计算,以 i i i为结尾的 1 ∼ i 1\sim i 1∼i位置的最长上升子序列 p r e [ i ] pre[i] pre[i],以及以 i i i开头的 i ∼ n i\sim n i∼n位置的最长上升子序列 s u f [ i ] suf[i] suf[i],而计算这个的方法就是常见的求最长上升子序列方法
-
而最长上升子序列长度是 m x = m a x { p r e [ i ] + s u f [ i ] − 1 } mx = max\{pre[i]+suf[i]-1\} mx=max{pre[i]+suf[i]−1},对于每个位置 p r e [ i ] + s u f [ i ] − 1 = m x pre[i]+suf[i]-1=mx pre[i]+suf[i]−1=mx,可以利用 n u m [ p r e [ i ] ] num[pre[i]] num[pre[i]]来记录可以组成 m x mx mx前缀长度的个数。对于某个 n u m [ j = = p r e [ i ] ] num[j==pre[i]] num[j==pre[i]]若为 1 1 1,则说明 i i i位置是所有最长上升子序列不可缺少的部分
-
对于每次询问 { a , b } \{a, b\} {a,b},首先求包含该更新位置的最长上升子序列长度: l e n = m a x i < a , h [ i ] < b ( p r e [ i ] ) + 1 + m a x j > a , h [ j ] > b ( s u f [ j ] ) len=max_{i<a,h[i]<b}(pre[i])+1+max_{j>a,h[j]>b}(suf[j]) len=maxi<a,h[i]<b(pre[i])+1+maxj>a,h[j]>b(suf[j])
-
对于这两个 m a x max max的求法,可以先将询问 b b b以及 h [ i ] h[i] h[i]进行离散化离线处理,然后利用线段树来处理即可,将 h [ i ] h[i] h[i]以及询问 b b b当作下标, p r e [ i ] o r s u f [ i ] pre[i] \, or \, suf[i] pre[i]orsuf[i]当作权值
-
接下来看 a a a位置,未更新之前,是否是最长上升子序列不可缺少的部分
-
倘若不是:则答案是 m a x ( m x , l e n ) max(mx,len) max(mx,len)
-
否则,答案是 m a x ( m x − 1 , l e n ) max(mx-1,len) max(mx−1,len)
AC代码:
#include <bits/stdc++.h>
#define ft first
#define sd second
#define pb push_back
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) //不能跟puts混用
#define seteps(N) fixed << setprecision(N)
#define endl "\n"
const int maxn = 8e5 + 10;
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
int n, m;
int h[maxn], pre[maxn], suf[maxn]; //pre[i]代表1~i一定以i结尾的最长上升子序列长度,suf[i]代表i~n一定以i开头的最长上升子序列长度
int num[maxn]; //num[i]代表当前长度可以组成最长上升子序列长度的个数
int a[maxn], cnta;
struct Qes {
int a, b, id;
bool operator < (Qes qs1) {return a < qs1.a;}
} qes[maxn];
struct SegTree {
int mx[maxn << 2];
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define mc (lc + rc >> 1)
void pushup(int rt) {mx[rt] = max(mx[ls], mx[rs]);}
void update_point(int lc, int rc, int rt, int x, int y) {
if (lc == rc) {
mx[rt] = max(mx[rt], y);
return;
}
if (x <= mc) update_point(lc, mc, ls, x, y);
else update_point(mc + 1, rc, rs, x, y);
pushup(rt);
}
int query(int lc, int rc, int rt, int L, int R) {
if (L > R) return 0;
if (L <= lc && rc <= R) return mx[rt];
if (R < lc || rc < L) return 0;
int res = 0;
res = max(query(lc, mc, ls, L, R), query(mc + 1, rc, rs, L, R));
return res;
}
} sgt1, sgt2; //sgt1存的是前缀,sgt2存的是后缀长度
void init1() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &h[i]), a[++cnta] = h[i];
for (int i = 1; i <= m; i++) scanf("%d%d", &qes[i].a, &qes[i].b), qes[i].id = i, a[++cnta] = qes[i].b;
sort (qes + 1, qes + m + 1);
sort (a + 1, a + cnta + 1);
cnta = unique(a + 1, a + cnta + 1) - (a + 1);
for (int i = 1; i <= n; i++) h[i] = lower_bound(a + 1, a + cnta + 1, h[i]) - a;
for (int i = 1; i <= m; i++) qes[i].b = lower_bound(a + 1, a + cnta + 1, qes[i].b) - a;
}
int d[maxn], lend, mx;
void init2() {
/* 处理pre */
for (int i = 1; i <= n; i++) {
if (h[i] > d[lend]) d[++lend] = h[i], pre[i] = lend;
else {
int p = lower_bound(d + 1, d + lend + 1, h[i]) - d;
pre[i] = p;
d[p] = h[i];
}
}
/* 处理suf, 相当于从后往前求最长下降子序列,倘若把h[i]变为负数,就变成求最长上升子序列*/
lend = 0;
d[0] = -1e9 - 10;
for (int i = n; i >= 1; i--) {
if (-h[i] > d[lend]) d[++lend] = -h[i], suf[i] = lend;
else {
int p = lower_bound(d + 1, d + lend + 1, -h[i]) - d;
suf[i] = p;
d[p] = -h[i];
}
}
/* 处理num */
mx = 0;
for (int i = 1; i <= n; i++) mx = max(mx, pre[i] + suf[i] - 1);
for (int i = 1; i <= n; i++) {
if (pre[i] + suf[i] - 1 == mx)
num[pre[i]]++;
}
}
int res[maxn];
void solve() {
int p = 1;
for (int i = 1; i <= m; i++) res[i] = 1;
for (int i = 1; i <= m; i++) {
while (p + 1 <= qes[i].a) {//把qes[i].a前的位置都更新
sgt1.update_point(1, cnta, 1, h[p], pre[p]);
p++;
}
res[qes[i].id] += sgt1.query(1, cnta, 1, 1, qes[i].b - 1);
}
p = n;
for (int i = m; i >= 1; i--) {
while (p - 1 >= qes[i].a) { //把qes[i].b后的位置都更新
sgt2.update_point(1, cnta, 1, h[p], suf[p]);
p--;
}
res[qes[i].id] += sgt2.query(1, cnta, 1, qes[i].b + 1, cnta);
}
for (int i = 1; i <= m; i++) {
if (num[pre[qes[i].a]] == 1 && pre[qes[i].a] + suf[qes[i].a] - 1 == mx) res[qes[i].id] = max(res[qes[i].id], mx - 1);
else res[qes[i].id] = max(res[qes[i].id], mx);
}
for (int i = 1; i <= m; i++) cout << res[i] << endl;
}
int main() {
init1(); //输入以及离散化
init2(); //求pre,suf以及num
solve();
return 0;
}
/*
15 1
76 9 32 82 40 91 46 5 12 69 44 97 30 13 29
4 73
*/