【题目描述】
给定一个序列,有两个操作。
U--将第A个数改成B。
Q--询问[a,b]区间内最长的连续上升子序列。
【算法分析】
线段树的区间合并。
s[n][0]表示以区间左端点开头的上升子序列长度,s[n][1]表示以区间右端点结尾的上升子序列长度s[n][2]表示整个区间最长的上升子序列长度。
ps.更新s[n][2]的地方写的不够简洁,还能优化一下代码。
#include<cstdio>
#define N 100010
#define lson l,m,n<<1
#define rson m+1,r,n<<1|1
using namespace std;
int s[N<<2][3];
int x[N];
int max(int a,int b,int c){
int ans1=a>b?a:b;
return ans1>c?ans1:c;
}
int min(int a,int b){
return a<b?a:b;
}
void build(int l,int r,int n){
if(l==r){
s[n][0]=1,s[n][1]=1,s[n][2]=1;
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
s[n][0]=s[n<<1][0];
s[n][1]=s[n<<1|1][1];
int k=0;
if(x[m]<x[m+1]){
if(s[n<<1][0]==m-l+1)//左区间左端点的序列长贯穿整个左区间
s[n][0]+=s[n<<1|1][0];
if(s[n<<1|1][1]==r-m)//同上,判断右区间
s[n][1]+=s[n<<1][1];
k=s[n<<1][1]+s[n<<1|1][0];
}
else
k=max(s[n<<1][1],s[n<<1|1][0],k);
s[n][2]=max(s[n][0],s[n][1],k);
s[n][2]=max(s[n][2],s[n<<1][2],s[n<<1|1][2]);
}
void update(int nn,int l,int r,int n){
if(l==r)
return;
int m=(l+r)>>1;
if(nn<=m)
update(nn,lson);
else
update(nn,rson);
s[n][0]=s[n<<1][0];
s[n][1]=s[n<<1|1][1];
int k=0;
if(x[m]<x[m+1]){
if(s[n<<1][0]==m-l+1)
s[n][0]+=s[n<<1|1][0];
if(s[n<<1|1][1]==r-m)
s[n][1]+=s[n<<1][1];
k=s[n<<1][1]+s[n<<1|1][0];
}
else
k=max(s[n<<1][1],s[n<<1|1][0],k);
s[n][2]=max(s[n][0],s[n][1],k);
s[n][2]=max(s[n][2],s[n<<1][2],s[n<<1|1][2]);
}
int query(int ll,int rr,int l,int r,int n){
if(ll==l&&rr==r)
return s[n][2];
int m=(l+r)>>1;
if(rr<=m)
return query(ll,rr,lson);
else if(ll>m)
return query(ll,rr,rson);
else{
int k1=query(ll,m,lson);
int k2=query(m+1,rr,rson);
int k3=0;
if(x[m]<x[m+1])
k3=min(s[n<<1][1],m-ll+1)+min(s[n<<1|1][0],rr-m);
return max(k1,k2,k3);
}
}
int main(){
// freopen("in.txt","r",stdin);
int t,m,n;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&x[i]);
build(1,n,1);
for(int i=1;i<=m;i++){
char c[5];
int a,b;
scanf("%s%d%d",c,&a,&b);
if(c[0]=='U'){
x[a+1]=b;
update(a+1,1,n,1);
}
else if(c[0]=='Q'){
printf("%d\n",query(a+1,b+1,1,n,1));
}
}
}
return 0;
}
浙公网安备 33010602011771号