【题目描述】
给定一个序列,有两个操作。
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; }