Description
Given n integers.
You have two operations: U A B: replace the Ath number by B. (index counting from 0) Q A B: output the length of the longest consecutive increasing subsequence (LCIS) in [a, b]. Input
T in the first line, indicating the case number.
Each case starts with two integers n , m(0<n,m<=10 5). The next line has n integers(0<=val<=10 5). The next m lines each has an operation: U A B(0<=A,n , 0<=B=10 5) OR Q A B(0<=A<=B< n). Output
For each Q, output the answer.
Sample Input
Sample Output
Source
HDOJ Monthly Contest � 2010.02.06
| ![]() |
运用线段树求最长递增子序列长度,可以想象,线段树一个节点存的主要信息:左起最大连续长度,右起最大连续长度,中间最大连续长度。。。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define inf -0x3f3f3f3f
#define mem0(a) memset(a,0,sizeof(a))
const int maxn = 100000+10;
struct node {
int l,r;
int lsum,rsum,msum;
}a[maxn<<2];
int b[maxn];
inline void pushup(int cur){
a[cur].msum = max(a[cur<<1].msum,a[cur<<1|1].msum);
a[cur].lsum = a[cur<<1].lsum ;
a[cur].rsum = a[cur<<1|1].rsum ;
if(b[a[cur<<1].r] < b[a[cur<<1|1].l]){
//如果左子树的右边界值小于右子树的左边界值,要合并左子树的右边界和右子树的左边界进行计算
a[cur].msum = max(a[cur].msum,a[cur<<1].rsum+a[cur<<1|1].lsum);
//左子树右边最大连续长度+右儿子左边最大连续长度
//这两个条件都是建立在左子树右边值小于右子树左边值的基础上,否则无需更新
if(a[cur<<1].lsum == a[cur<<1].r - a[cur<<1].l +1)//该节点左子树左起最大连续长度等于区间长度
a[cur].lsum += a[cur<<1|1].lsum;//该节点左起最大连续长度为左子树长度+右子树左起最大长度
if(a[cur<<1|1].rsum == a[cur<<1|1].r - a[cur<<1|1].l +1)//节点右子树右起最大长度等于区间长度
a[cur].rsum += a[cur<<1].rsum ;//节点右起最大长度为右子树长度+左子树右起最大长度
}
}
void build(int l,int r,int cur){
a[cur].l = l ;
a[cur].r = r ;
if(l == r){
scanf("%d",&b[l]);
a[cur].lsum = a[cur].rsum = a[cur].msum = 1;
return ;
}
int mid = ( l + r)>>1;
build(l,mid,cur<<1);
build(mid+1,r,cur<<1|1);
pushup(cur);
}
void update(int x,int v, int cur){
if(a[cur].l == a[cur].r && x == a[cur].l){
b[x] = v;
return ;
}
int mid = (a[cur].l + a[cur].r )>>1;
if(x <= mid)
update(x,v,cur<<1);
else
update(x,v,cur<<1|1);
pushup(cur);
}
int query(int l,int r,int cur){
if(l <= a[cur].l && r >= a[cur].r)
return a[cur].msum ;
int mid = (a[cur].l+ a[cur].r)>>1;
if(r <= mid)
return query(l,r,cur<<1);//最大lcis在左子树
else if( l > mid )
return query(l,r,cur<<1|1);//在右子树
else {//最大lcis在中间,跨越两个子树
int ans = 0 ;
if(b[a[cur<<1].r] < b[a[cur<<1|1].l]){//当左子树右端点小于右子树左端点时
int ll = a[cur<<1].rsum ,rr = a[cur<<1|1].lsum;
if( ll > mid - l +1)//防止rsum/lsum长度大于当前区间长度
//之前更新的lsum/rsum可能接到另一边,长度可能大于当前区间长度
ll = mid - l + 1;
if(rr > r - (mid+1) +1)
rr = r - (mid+1) +1;
ans = max(ans,ll + rr);//求中间最长lcis长度
}
ans = max(ans,query(l,mid,cur<<1));//与左边最大LCIS长度
ans = max(ans,query(mid+1,r,cur<<1|1));//再与右边最大LCIS长度
//这种情况是查询区间正好横跨左右子树,但左右子树相邻点并不递增
//比如左子树 5 6 7 ,右子树为 1 3 6,得分开比较
return ans ;
}
}
int main()
{
int T,n,m;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
build(1,n,1);
char s[3];
// for(int i = 1 ; i <= n ; i++)
// printf("%d ",b[i]);
while(m--){
int a,b;
scanf("%s%d%d",s,&a,&b);
if(s[0]=='Q'){//题上编号是从0开始,为方便计算,统一加1不影响结果
printf("%d\n",query(a+1,b+1,1));
}
else if(s[0]=='U'){
update(a+1,b,1);
}
}
}
return 0;
}