题意:
给一个长度为n的序列 ai ,有m个询问,一次询问给出 pos,val ,问假如把 apos 变成 val ,序列的最长上升子序列的长度(设为 lis )是多少,注意询问不会改变原序列。
题解:
把a_{pos}
apos
变成
val
之后,通过思考可以发现单次询问时有两种情况,第一种是答案的
lis
中使用了
val
,第二种是没有使用。
首先考虑使用了,那么需要一些信息,
lsa[i]
表示以
i
为结尾的最长
容易发现
lsa和lsb
都可以通过离线,在求原序列
lis
时求出来,正着求一次,倒着求一次就可以了,但是要注意询问里,
i
位置的
当处理某个询问时,使用了
val
的答案就是
lsa[i]+lsb[i]−1
。
另外一种情况就是没有使用到
val
,就是判断
apos
在原序列的
lis
中是不是必须出现,如果可以被替代,那么答案就是
lis
,如果不可以被替代,答案是
lis−1
。
判断
lis
中哪些元素一定出现,就是这个题。
方法也是求出原序列的
lsa和lsb
,并且判断是否存在
i≠j
且
lsa[i]==lsa[j]且lsa[i]+lsb[i]==lis−1且lsa[j]+lsb[j]==lis−1
。
如果存在则说明
i
和
这样两种情况就考虑完了。
#include<bits/stdc++.h>
using namespace std;
const int N = 4e5+5;
const int inf = ~0u>>2;
int a[N];
struct Q{
int val, id;
Q(){}
Q(int b, int c){ val = b, id = c; }
};
vector<Q>qry[N];
int lsa[N], lsb[N];
int ansla[N], anslb[N];
int ans[N];
int p[N], pcnt;
int n, m;
int cs[N];
bool ok[N];
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) scanf("%d", a+i);
for(int i = 1; i <= m; ++i){
int pos, val;
scanf("%d%d", &pos, &val);
qry[pos].push_back(Q(val, i));
}
p[0] = -inf, pcnt = 1;
for(int i = 1; i <= n; ++i){
for(int k = qry[i].size()-1; k >= 0; --k){
int pos = lower_bound(p, p+pcnt, qry[i][k].val)-p;
ansla[qry[i][k].id] = pos;
}
int pos = lower_bound(p, p+pcnt, a[i])-p;
lsa[i] = pos;
p[pos] = a[i];
if(pos == pcnt) ++pcnt;
}
int lis = pcnt-1;
p[0] = -inf, pcnt = 1;
for(int i = n; i >= 1; --i){
for(int k = qry[i].size()-1; k >= 0; --k){
int pos = lower_bound(p, p+pcnt, -qry[i][k].val)-p;
anslb[qry[i][k].id] = pos;
}
int pos = lower_bound(p, p+pcnt, -a[i])-p;
lsb[i] = pos;
p[pos] = -a[i];
if(pos == pcnt) ++pcnt;
}
memset(cs, -1, sizeof(cs));
for(int i = 1; i <= n; ++i){
if(lsa[i] + lsb[i] == lis+1){
if(cs[lsa[i]] == -1) cs[lsa[i]] = i;
else cs[lsa[i]] = -2;
}
}
for(int i = 1; i <= lis; ++i) if(cs[i] > 0) ok[cs[i]] = 1;
for(int i = 1; i <= n; ++i){
for(int k = 0; k < qry[i].size(); ++k){
ans[qry[i][k].id] = max(ok[i]?lis-1:lis, ansla[qry[i][k].id]+anslb[qry[i][k].id]-1);
}
}
for(int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
}