LCIS(线段树+区间合并)

题目链接:Problem - 3308 (hdu.edu.cn)

题意描述:给出n个数。然后给出多个操作,有修改操作和查询操作,查询操作给出区间左右端点,输出区间中最长连续上升子序列的长度。

这道题难就难在pushup操作上,其他就是一个正常的线段树操作,我们需要维护一个区间的3个状态量,一个是以左端点开始的最长连续上升子序列的长度,另一个是以右端点结尾的最长连续上升子序列的长度,还有一个是本区间的最长连续上升子序列。更新的话需要注意的一点就是最长连续上升子序列可能分布在两个待合并区间上,所以就需要判断一下,其他没什么了,代码中有详解:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int N=1e6+10;
//head表示以头节点开始的最长连续子序列
//tail表示以尾节点结束的最长连续子序列
//maxn表示区间中最长连续子序列 
int a[N],head[N],tail[N],maxn[N],l[N],r[N];
void pushup(int id)
{
	//如果以左区间头节点开始的最长连续子序列就等于左区间长度,那么合并后区间的以头节点开始的最长连续子序列的长度将可能会发生改变 
	if((head[id<<1]==r[id<<1]-l[id<<1]+1)&&(a[r[id<<1]]<a[r[id<<1]+1]))
		head[id]=head[id<<1]+head[id<<1|1];
	else head[id]=head[id<<1];
	//如果以右区间尾节点结尾的最长连续子序列就等于右区间长度,那么合并后区间的以尾节点结尾的最长连续子序列的长度将可能会发生改变 
	if((tail[id<<1|1]==r[id<<1|1]-l[id<<1|1]+1)&&(a[r[id<<1]]<a[r[id<<1]+1]))
		tail[id]=tail[id<<1]+tail[id<<1|1];
	else tail[id]=tail[id<<1|1];
	maxn[id]=max(maxn[id<<1],maxn[id<<1|1]);
	//还要考虑合并后区间中间的最长连续子序列情况 
	if(a[r[id<<1]]<a[r[id<<1]+1])
		maxn[id]=max(maxn[id],tail[id<<1]+head[id<<1|1]);
}
void build(int id,int L,int R)
{
	l[id]=L;r[id]=R;
	if(L==R)
	{
		maxn[id]=1;
		head[id]=1;
		tail[id]=1;
		return ;
	}
	int mid=L+R>>1;
	build(id<<1,L,mid);
	build(id<<1|1,mid+1,R);
	pushup(id);
}
void update_point(int id,int x,int val)
{
	if(l[id]==r[id])
	{
		a[x]=val;
		return ;
	}
	int mid=l[id]+r[id]>>1;
	if(x<=mid) update_point(id<<1,x,val);
	else update_point(id<<1|1,x,val);
	pushup(id);
}
int query_interval(int id,int L,int R)
{
	if(l[id]>R||r[id]<L) return 0;
	if(l[id]>=L&&r[id]<=R) return maxn[id];
	int ans=max(query_interval(id<<1,L,R),query_interval(id<<1|1,L,R));
	if(a[r[id<<1]]<a[r[id<<1]+1]) ans=max(ans,min(tail[id<<1],r[id<<1]-L+1)+min(head[id<<1|1],R-l[id<<1|1]+1));
	return ans;
}
int main()
{
	int T,n,q;
	char op[10];
	int c,d;
	cin>>T;
	while(T--)
	{
		scanf("%d%d",&n,&q);
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
		build(1,1,n);
		while(q--)
		{
			scanf("%s%d%d",op,&c,&d);
			if(op[0]=='Q')
				printf("%d\n",query_interval(1,c+1,d+1));
			else
				update_point(1,c+1,d);
		}
	}
	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值