HDU 3308 LCIS (线段树合并区间)

本文介绍了一个关于最长连续递增子序列(LCIS)的问题及其解决方案。通过使用线段树的数据结构来实现高效的区间合并操作,解决了在序列上进行数值更新和查询LCIS长度的操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LCIS

Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 8219    Accepted Submission(s): 3519


Problem 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<=105).
The next line has n integers(0<=val<=105).
The next m lines each has an operation:
U A B(0<=A,n , 0<=B=105)
OR
Q A B(0<=A<=B< n).
 

Output
For each Q, output the answer.
 

Sample Input
1 10 10 7 7 3 3 5 9 9 8 1 8 Q 6 6 U 3 4 Q 0 1 Q 0 5 Q 4 7 Q 3 5 Q 0 2 Q 4 6 U 6 10 Q 0 9
 

Sample Output
1 1 4 2 3 1 2 5

 


题意:

给你一串序列,有两个操作1,将里面第a个数改成b

2 输出下标在[a,b]内的连续最长递增子序列


解析:

用线段树的合并区间来做,主要就是在pushup里面进行操作

还有是查询的时候"pushup"也要重写


#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 400010
#define INF 999999999
using namespace std;


typedef struct TREE
{
	int rlen;   //从左边端点l开始满足条件的最大区间长度(包括r),在点表示的区间内
	int llen;    //从左边端点l开始满足条件的最大区间长度(包括l),在该点表示的区间内
	int all;    //rt对应区间即[l,r]内满足条件的区间的最大长度
}seg;
seg Btree[MAXN];
int stu[MAXN];

int MAX(int a,int b)
{
	return a<b?b:a;
}

int MIN(int a,int b)
{
	return a<b?a:b;
}

void pushup(int l,int r,int root)
{
	Btree[root].llen=Btree[2*root].llen;  //从最左边向右算起
	Btree[root].rlen=Btree[2*root+1].rlen;   //右孩子的右边向左算起
	Btree[root].all=MAX(Btree[root*2].all,Btree[root*2+1].all);

	int mid=(l+r)>>1;
	int len=r-l+1;
	if(stu[mid]<stu[mid+1])  //合并
	{                                    //因为要求的区间是连续的所以必须等于区间长度
		if(Btree[root].llen==len-len/2)   //lsum[rt<<1]也就是左子区间左边开始的最大长度,如果这个区间长度刚好是l到mid之间的长度
		{                                 //,(又因为可以合并,所以可以更新[root].llen)说明区间已经穿过中点了,应该在加上右子区间lsum[rt<<1|1]这部分
			Btree[root].llen+=Btree[2*root+1].llen;
		}
		if(Btree[root].rlen==len/2)
		{
			Btree[root].rlen+=Btree[2*root].rlen;
		}
		Btree[root].all=MAX(Btree[root].all,Btree[root*2+1].llen+Btree[root*2].rlen);   //要中间连起来,则必须从左孩子的最右边向左数+右孩子的最左边向右数(连续)
	}
}

void build(int stu[],int l,int r,int root)  //l,r表示他们在stu中的下标,root表示他们在线段树中的坐标
{                                      //不是一颗完全二叉树
	if(l>r)return;
	if(l==r)
	{
		Btree[root].llen=Btree[root].rlen=Btree[root].all=1;
        return;
	}

	int mid=(l+r)/2;

	build(stu,l,mid,root*2);
	build(stu,mid+1,r,root*2+1);

	pushup(l,r,root);
}

void updateone(int root,int l,int r,int index,int val)
{
	if(l>r)return;
	if(l==r)
	{
		//if(l==index)   //可有可无
			//Btree[root].all=Btree[root].llen=Btree[root].rlen=1;
		return;   //pushup会重新进行更新上面的值,因为叶子结点的值永远是1,1,1,所以可有可无
	}

	int mid=(l+r)/2;
	if(index<=mid)
		updateone(root*2,l,mid,index,val);
	else
		updateone(root*2+1,mid+1,r,index,val);

	pushup(l,r,root);
}

int query(int root,int s1,int e1,int l,int r)
{
	if(s1>r||e1<l)return 0;
	if(s1>=l&&e1<=r)
	{
		return Btree[root].all;
	}

	int mid=(s1+e1)/2;              //主要3.对区间的查找
	if(mid>=r)
	return query(root*2,s1,mid,l,r);
	else if(mid+1<=l)
	return query(root*2+1,mid+1,e1,l,r);
	else
    {
        int lan=query(root*2,s1,mid,l,r);
        int ran=query(root*2+1,mid+1,e1,l,r);
        int sum=0;
        if(stu[mid]<stu[mid+1])
        {
            sum=MIN(Btree[root<<1].rlen,mid-l+1)+MIN(Btree[root<<1|1].llen,r-mid);
            //这里主要是求中间区域的因为s1<l<mid<e<e2,所以对于左孩子的后缀长度来说,只有在[l,mid]内是有效的,右边同理
        }
        return MAX(MAX(lan,ran),sum);

    }

}

int main()
{
	int i,n,m,a,b,t,ans;
    scanf("%d",&t);
	while(t--)
	{
	    scanf("%d%d",&n,&m);
		memset(Btree,0,sizeof(Btree));
		for(int i=1;i<=n;i++)
        {
            scanf("%d",&stu[i]);
        }
		build(stu,1,n,1);
		char c;
		getchar();
		for(i=1;i<=m;i++)
		{
		    getchar();
			scanf("%c%d%d",&c,&a,&b);
			if(c=='Q')
			{
			    a++;
			    b++;
				ans=query(1,1,n,a,b);
				printf("%d\n",ans);
			}
			else
			{
			    a++;
			    stu[a]=b;
				updateone(1,1,n,a,b);
			}
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值