单点更新,区间合并,最长连续上升子序列
HH神总结的线段树专辑:https://wenku.baidu.com/view/71fc1659ba1aa8114431d97b.html
里面给的练习,看懂里面那个例题,做这个题就很简单了
#include <cstdio>
#include <cstring>
#include <algorithm>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int MAXN = 100010;
int num[MAXN];
//lsum表示从左边起的最长连续上升子序列,rsum同样
//msum是整个区间内的最长连续上升子序列
int lsum[MAXN<<2],rsum[MAXN<<2],msum[MAXN<<2];
//单点更新,只要pushup就可以了
void pushUp(int l, int r, int rt)
{
int m = r-l+1;
int mid = (l+r)>>1;
//先根据子节点更新父节点
msum[rt] =std::max(msum[rt<<1],msum[rt<<1|1]);
lsum[rt] = lsum[rt<<1];
rsum[rt] = rsum[rt<<1|1];
//然后合并区间
if(lsum[rt] == m-(m>>1) && num[mid+1] > num[mid])
lsum[rt] += lsum[rt<<1|1];
if(rsum[rt] == m>>1 && num[mid+1] > num[mid])
rsum[rt] += rsum[rt<<1];
if(num[mid] < num[mid+1])
msum[rt] = std::max(msum[rt],rsum[rt<<1]+lsum[rt<<1|1]);
}
void build(int l, int r, int rt)
{
if(l == r)
{
//数据的输入放在这里,我放在主函数内先输入后建树会超时
lsum[rt] = rsum[rt] = msum[rt] = 1;
scanf("%d",&num[l]);
return;
}
int m = (l+r) >> 1;
build(lson);
build(rson);
pushUp(l,r,rt);
}
void update(int l, int r, int rt, int cur, int now)
{
if(l == r)
{
num[l] = now;
return;
}
int m = (l+r) >> 1;
if(cur <= m)
update(lson,cur,now);
else
update(rson,cur,now);
pushUp(l,r,rt);
}
int query(int L, int R, int l, int r, int rt)
{
if(L <= l && r <= R)
return msum[rt];
int m = (l+r) >> 1;
if(R <= m)
return query(L,R,lson);
else if(L > m)
return query(L,R,rson);
else
{
int ret = std::max(query(L,m,lson),query(m+1,R,rson));
if(num[m+1] > num[m])
//前边wa了好几次,忘了这里这个条件了,不仅要计算合并的区间,还要限制住区间的长度在询问范围内
ret = std::max(ret,std::min(lsum[rt<<1|1],R-m)+std::min(rsum[rt<<1],m-L+1));
return ret;
}
}
int main()
{
char com;
int t,n,m,a,b;
scanf("%d",&t);
while(t--)
{
scanf("%d %d",&n,&m);
build(1,n,1);
for(int i = 0; i < m; ++i)
{
scanf(" %c",&com);
if(com == 'U')//合并区间
{
scanf("%d %d",&a,&b);
update(1,n,1,a+1,b);
}
else//区间查询
{
scanf("%d %d",&a,&b);
int p = query(a+1,b+1,1,n,1);
printf("%d\n",p);
}
}
}
return 0;
}