LCIS

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<=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
  
  
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


题解:求区间连续递增的最大个数,用到了线段树的区间合并。每个节点保存了该区间的最大递增数,该区间的左值和右值,这两个变量用来判断两个孩子节点构成的递增

序列,再通过左孩子的最右端的最大递减序列+右孩子的最左端的最大递增序列,就可以更新父节点的最大递增序列了。自己画图好好体会就行了。


#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

struct Node
{
	int l,r;     //左右区间 
	int ln,rn;   //区间的左值和右值,因为需要拿来和其他区间比较是不是能构成连续递增序列 
	int ls,rs,ms; //左值的最大连续递增个数和右值最大连续递减个数,和该区间最大连续递增个数 
};

Node arr[400005];
int a[100005];

int max(int x,int y)
{
	return x > y ? x : y;
}

int min(int x,int y)
{
	return x > y ? y : x;
}

void pushUp(int k)     //将父节点的变量更新,因为儿子节点都知道了 
{
	int li = k << 1;
	int ri = (k << 1) | 1;
	int mid = (arr[k].l + arr[k].r) >> 1;
	arr[k].ln = arr[li].ln;
	arr[k].rn = arr[ri].rn;
	arr[k].ls = arr[li].ls;
	arr[k].rs = arr[ri].rs;
	arr[k].ms = max(arr[li].ms,arr[ri].ms); //取孩子较大的序列 
	if(arr[li].rn < arr[ri].ln)             //如果左孩子的最右边的值小于右孩子的最左边的值 
	{                                       //计算这个序列 
	    if(arr[li].ls == arr[li].r - arr[li].l + 1) //更新左递增序列 
		{
		    arr[k].ls += arr[ri].ls;	
		}	
		if(arr[ri].ls == arr[ri].r - arr[ri].l + 1) //更新右递减序列 
		{
			arr[k].rs += arr[li].rs;
		}
		
		arr[k].ms = max(arr[k].ms,arr[li].rs + arr[ri].ls); //得到最大连续递增序列个数 
	} 
}



void segTree(int k,int l,int r)
{
	arr[k].l = l;
	arr[k].r = r;
	if(l == r)
	{
		arr[k].ln = arr[k].rn = a[l];
		arr[k].ls = arr[k].rs = arr[k].ms = 1;
		return;
	}
	int mid = (l + r) >> 1;
	segTree(k << 1,l,mid);
	segTree((k << 1) | 1,mid + 1,r);
	pushUp(k);     //更新父节点 
}

void insert(int k,int x,int y)
{
	if(arr[k].l == arr[k].r)
	{
		arr[k].ln = arr[k].rn = y;
		return;
	}
	int mid = (arr[k].l + arr[k].r) >> 1;
	if(x > mid)
	{
		insert((k << 1) | 1,x,y);
	}
	else
	{
		insert(k << 1,x,y);
	}
	pushUp(k);
}

int query(int k,int l,int r)
{
	if(arr[k].l == l && arr[k].r == r)
	{
		return arr[k].ms;
	}
	int li = k << 1;
	int ri = (k << 1) | 1;
	int mid = (arr[k].l + arr[k].r) >> 1;
	
	if(l > mid)
	{
		return query(ri,l,r);
	}
	if(r <= mid)
	{
		return query(li,l,r);
	}
	int t1 = query(li,l,mid);
	int t2 = query(ri,mid + 1,r);
	int t3 = 0;
	if(arr[li].rn < arr[ri].ln)   //左孩子和右孩子还能构成递增序列 
	{
		t3 = min(mid - l + 1,arr[li].rs) + min(r - mid,arr[ri].ls); //找到左孩子右递减部分和右孩子左递增部分 
	}
	
	return max(max(t1,t2),t3);  //取最大值 
	
}

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i = 1;i <= n;i++)
		{
			scanf("%d",&a[i]);
		}
		segTree(1,1,n);      //建树 
		
		char ch[3];
		int l,r;
		for(int i = 0;i < m;i++)
		{
			scanf("%s%d%d",ch,&l,&r);
			if(ch[0] == 'Q')
			{
				printf("%d\n",query(1,l + 1,r + 1));  //下标从1开始算 
			}
			else
			{
				insert(1,l + 1,r);
			}
		}
	}
	
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值