题意: 多组输入,对于每一个样例,第一行有两个整数:n,m。第二行有一个长度为n的序列。接下来m行为m次操作。对于每一次的查询操作,输出给定区间的最长连续上升子序列的长度,对于每一次的修改操作,将给定位置的整数替换为指定的整数.
思路:线段树 单点更新 区间合并 详情看代码。
#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int a[N], lm[N<<2], mm[N<<2], rm[N<<2];
//a[]数组即为给定数组,lm[] mm[] rm[]数组分别表示当前节点对应区间从左端点开始LCIS,整个区间的LCIS,在右端点结束的LCIS的长度
void pushUp(int rt, int l,int r)
{
int m = r-l+1, mid = (l+r)>>1;
lm[rt] = lm[rt<<1], rm[rt] = rm[rt<<1|1];
if(lm[rt] == m - (m>>1) && a[mid] < a[mid+1]) lm[rt] += lm[rt<<1|1];
if(rm[rt] == m>>1 && a[mid] < a[mid+1]) rm[rt] += rm[rt<<1];
if(a[mid] < a[mid+1]) //父节点的值的来源只有三处,左儿子的最大值,右儿子的最大值,以及中间的连续上升子序列的长度
mm[rt] = max(rm[rt<<1] + lm[rt<<1|1], max(mm[rt<<1], mm[rt<<1|1]));//
else //当中间的连续序列不满足递增的条件时,来源只有两处
mm[rt] = max(mm[rt<<1], mm[rt<<1|1]);
}
void build(int l,int r,int rt)
{
if(l == r){
scanf("%d", &a[l]);
lm[rt] = mm[rt] = rm[rt] = 1;
return ;
}
int m = (l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
pushUp(rt,l,r);
}
void update(int L,int C,int l,int r,int rt)
{
if(l == r){
a[l] = C;
return ;
}
int m = (l+r)>>1;
if(L <= m) update(L,C,l,m,rt<<1);
else update(L,C,m+1,r,rt<<1|1);
pushUp(rt,l,r);
}
int query(int L,int R,int l,int r,int rt) //结果只有两个来源:线段树某子节点对应的区间中的最大值,以及跨区间的LCIS
{
if(L <= l && r <= R)
return mm[rt];
int m = (l+r)>>1, ans = 0;
if(L <= m && m < R && a[m] < a[m+1]){
ans = min(m-L+1, rm[rt<<1]) + min(R-m, lm[rt<<1|1]);
}
if(L <= m) ans = max(ans, query(L,R,l,m,rt<<1));
if(m < R) ans = max(ans, query(L,R,m+1,r,rt<<1|1));
return ans;
}
int main()
{
int T, n, m, a, b;
char oper[5];
scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &m);
build(1,n,1);
while(m--){
scanf("%s%d%d", oper, &a, &b);
if(oper[0] == 'Q')
printf("%d\n", query(a+1,b+1,1,n,1));
else
update(a+1,b,1,n,1);
}
}
return 0;
}