<题目链接>
题目大意:
给你一段序列,对其进行两种操作,一是修改某个序号的点的值;二是查询某个区间的LCIS(最长上升子序列)。
解题分析:
线段树区间合并的典型例题,用求某个区间的LCIS时,需要比较三个值,一是左区间的LCIS,二是右区间的LCIS,三是左右子区间合并的LCIS。最重要的是第三点如何实现,实现第三点需要维护一个最长后缀上升子序列和最长前缀上升子序列,总之,相对于一般的线段树,区间合并需要对Pushup()函数进行一些改动,query()的时候也要记得对三种情况进行讨论。
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 const int siz=100005; 7 int a[siz],sum[siz<<2],lsum[siz<<2],rsum[siz<<2]; 8 9 void pushup(int rt,int l,int r) { //区间合并,更新该节点对应的最长上升子序列和最长前、后缀 10 lsum[rt]=lsum[rt<<1]; //最长前缀 11 rsum[rt]=rsum[rt<<1|1]; //最长后缀 12 sum[rt]=max(sum[rt<<1],sum[rt<<1|1]); 13 /*-- 先更新该节点需要维护的三个值 --*/ 14 15 int m=(l+r)>>1; 16 if(a[m]<a[m+1]){ //如果左右子区间的LCIS能够合并,那么就进一步更新这三个点 17 18 if(lsum[rt<<1]==(m-l+1)) //如果左子区间的最长前缀==左子区间长度 19 lsum[rt]+=lsum[rt<<1|1]; //那么该节点的最长前缀除左子区间的最长前缀以外,还要加上右子区间的最长前缀 20 if(rsum[rt<<1|1]==(r-m)) //如果右子区间的最长后缀==右子区间长度 21 rsum[rt]+=rsum[rt<<1]; //那么该节点的最长后缀除右子区间的最长后缀外,还要加上左子区间的最长后缀 22 /*-- 更新rt节点对应区间的最长前、后缀 --*/ 23 24 sum[rt]=max(sum[rt],rsum[rt<<1]+lsum[rt<<1|1]); 25 //更新rt节点对应区间的最长上升子序列 26 } 27 } 28 void build(int l,int r,int rt){ 29 if(l==r){ //注意这里的初始化 30 sum[rt]=1; 31 lsum[rt]=rsum[rt]=1; 32 return; 33 } 34 int m=(l+r)>>1; 35 build(l,m,rt<<1); 36 build(m+1,r,rt<<1|1); 37 pushup(rt,l,r); 38 } 39 void update(int loc,int val,int l,int r,int rt){ //单点更新 40 if(l==r){ 41 a[l]=val; 42 return; 43 } 44 int m=(l+r)>>1; 45 if(loc<=m) 46 update(loc,val,l,m,rt<<1); 47 else 48 update(loc,val,m+1,r,rt<<1|1); 49 pushup(rt,l,r); 50 } 51 int query(int L,int R,int l,int r,int rt){ 52 if(L<=l&&r<=R) 53 return sum[rt]; 54 int m=(l+r)>>1; 55 int ans=1; //注意这里ans初始化为1 56 57 /*-- 答案一共三种可能,在左区间或右区间或横跨两个区间 --*/ 58 if(L<=m) //左、右区间的LCIS 59 ans=max(ans,query(L,R,l,m,rt<<1)); 60 if(R>m) 61 ans=max(ans,query(L,R,m+1,r,rt<<1|1)); 62 63 if(L<=m&&R>=m&&a[m]<a[m+1]) //横跨左右两个子区间的情况 64 ans=max(ans,min(m-L+1,rsum[rt<<1])+min(R-m,lsum[rt<<1|1])); //rsum代表区间最长后缀、lsum为区间最长前缀 65 return ans; 66 } 67 68 int main(){ //lsum为区间左端点开始长度 69 char c; //rsum为区间右端点开始长度 70 int t,n,m; //sum为区间最长长度 71 scanf("%d",&t); 72 while(t--){ 73 scanf("%d%d",&n,&m); 74 for(int i=1;i<=n;i++) 75 scanf("%d",&a[i]); 76 build(1,n,1); 77 while(m--){ 78 int u,v; 79 cin>>c>>u>>v; 80 if(c=='Q'){ 81 u++,v++; //因为题目是从0开始编号 82 printf("%d\n",query(u,v,1,n,1)); 83 } 84 else{ 85 u++; 86 update(u,v,1,n,1); 87 } 88 } 89 } 90 return 0; 91 }
2018-09-11