D - Zip-line
题意翻译
给定一个长度为 n n n 的数组 a a a, m m m 次询问,每次询问如果把某位置的值换成另一个给定值,此数组的最长上升子序列(LIS)长度为多少。注意到询问间是独立的,不会真的进行修改。
题解
此题没有强制在线,所以我们不妨把询问离线下来。
离线下来后,我们就可以用我们熟知的树状数组(BIT)维护最大值的方法,先正着求一遍每个点以前的最长上升子序列,再倒着求一遍下降的子序列,中途就可以顺便在值域树状数组上处理包含询问位置的最长上升子序列。
然而仅仅时这样的话,还过不了样例一。我们发现上面的过程只算了包含询问节点的LIS,没算不包含询问节点的LIS。
但是这并不是什么大问题,我们可以分类讨论然后解决。容易发现不包含节点 i i i 的LIS只有3种情况:
- 前面第一个点比
a
i
a_i
ai 小,后面第一个点比
a
i
a_i
ai 大。这种情况很明显就是包含点
i
i
i 的LIS长度-1;
- 前面第一个点
≥
a
i
≥a_i
≥ai。此时我们设前面第一个点为
j
j
j,显然包含点
j
j
j 的LIS必然不会包含点
i
i
i。所以只需要再次用权值BIT对每个点
i
i
i 求出
max
j
<
i
,
a
j
≥
a
i
LIS
j
\max_{j<i,a_j≥a_i}\text{LIS}_j
maxj<i,aj≥aiLISj 。
- 后面第一个点
≤
a
i
\le a_i
≤ai。设后面第一个点为
j
j
j,同样地,包含
j
j
j 的LIS必然不包含点
i
i
i,所以倒着用权值BIT求出
max
j
>
i
,
a
j
≤
a
i
LIS
j
\max_{j>i,a_j\le a_i}\text{LIS}_j
maxj>i,aj≤aiLISj。
最后还需要统计一下答案。复杂度 O ( ( n + m ) log n ) O((n+m)\log n) O((n+m)logn)。
代码
#include<cstdio>//JZM yyds!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#define ll long long
#define uns unsigned
#define MAXN 400005
#define INF 1e18
#define MOD 9901ll
#define lowbit(x) ((x)&(-(x)))
#define IF it->first
#define IS it->second
using namespace std;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
return f?x:-x;
}
int n,m,k,h[MAXN],f[MAXN],ans[MAXN];
int pr[MAXN],sf[MAXN],mx[MAXN];
map<int,int>mp; //笔者习惯于用map离散化,虽然慢,但是方便又灵活
map<int,int>::iterator it;
inline void add(int x,int d){
for(;x<=k;x+=lowbit(x))f[x]=max(f[x],d);
}
inline int sch(int x){
int res=0;
for(;x>0;x-=lowbit(x))res=max(res,f[x]);
return res;
}
struct itn{
int h,i;itn(){}
itn(int H,int I){h=H,i=I;}
};
vector<itn>as[MAXN];
signed main()
{
n=read(),m=read();
for(int i=1;i<=n;i++)h[i]=read(),mp[h[i]]=1;
for(it=mp.begin();it!=mp.end();it++)IS=++k;
for(int i=1;i<=n;i++)h[i]=mp[h[i]];
for(int i=1;i<=m;i++){
int x=read(),y=read();
as[x].push_back(itn(y,i));
}
for(int i=1;i<=k;i++)f[i]=0;
for(int i=1;i<=n;i++){
for(uns j=0;j<as[i].size();j++){
int a=as[i][j].h;
it=mp.lower_bound(a);
if(it==mp.begin())continue;
it--,a=IS;
ans[as[i][j].i]+=sch(a);
}
pr[i]=sch(h[i]-1);
add(h[i],pr[i]+1);
}
for(int i=1;i<=k;i++)f[i]=0;
for(int i=n;i>0;i--){
for(uns j=0;j<as[i].size();j++){
int a=as[i][j].h;
it=mp.lower_bound(a+1);
if(it==mp.end())continue;
a=IS;
ans[as[i][j].i]+=sch(k-a+1);
}
sf[i]=sch(k-h[i]);
add(k-h[i]+1,sf[i]+1);
}
for(int i=1;i<=n;i++)mx[i]=pr[i]+sf[i];
for(int i=1;i<=k;i++)f[i]=0;
for(int i=1;i<=n;i++){
mx[i]=max(mx[i],sch(k-h[i]+1));
add(k-h[i]+1,pr[i]+sf[i]+1);
}
for(int i=1;i<=k;i++)f[i]=0;
for(int i=n;i>0;i--){
mx[i]=max(mx[i],sch(h[i]));
add(h[i],pr[i]+sf[i]+1);
}
for(int i=1;i<=n;i++)
for(uns j=0;j<as[i].size();j++){
int d=as[i][j].i;
ans[d]=max(ans[d],mx[i]-1);
}
for(int i=1;i<=m;i++)printf("%d\n",ans[i]+1);
return 0;
}