HDU-3308 线段树 区间合并

题意: 多组输入,对于每一个样例,第一行有两个整数: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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值