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

题目传送:LCIS

线段树,区间合并,一次过啦,没有纠结,这几天过的最愉快的一个题

思路:求最长连续上升子序列,外带单点更新,经典的线段树题目。具体看代码注释

AC代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <deque>
#include <cctype>
#define LL long long
#define INF 0x7fffffff
using namespace std;

int n, m;
const int maxn = 100005;

struct node {
	int sub;//表示当前结点最长连续上升子序列
	int lsub;//表示以最左边为开始的最长连续上升子序列
	int rsub;//表示以最右边为结束的最长连续上升子序列
	int l, r;//表示当前结点(一段区间)的最左位置的值和最右位置的值
}e[maxn << 2];

void pushup(int rt, int m) {//往上更新,略复杂
	e[rt].l = e[rt << 1].l;//更新最左
	e[rt].r = e[rt << 1 | 1].r;//更新最右
	e[rt].sub = max(e[rt << 1].sub, e[rt << 1 | 1].sub);//更新当前结点最长连续上升子序列
	if(e[rt << 1 | 1].l > e[rt << 1].r)
		e[rt].sub = max(e[rt].sub, e[rt << 1].rsub + e[rt << 1 | 1].lsub);

	e[rt].lsub = e[rt << 1].lsub;//更新以最左边为开始的最长连续上升子序列
	if(e[rt].lsub == m - (m >> 1) && e[rt << 1].r < e[rt << 1 | 1].l)
		e[rt].lsub += e[rt << 1 | 1].lsub; 

	e[rt].rsub = e[rt << 1 | 1].rsub;//更新以最右边为结束的最长连续上升子序列
	if(e[rt].rsub == (m >> 1) && e[rt << 1].r < e[rt << 1 | 1].l)
		e[rt].rsub += e[rt << 1].rsub;
}

void build(int l, int r, int rt) {//建树
	if(l == r) {
		int t;
		scanf("%d", &t);
		e[rt].l = e[rt].r = t;
		e[rt].sub = e[rt].lsub = e[rt].rsub = 1;
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, rt << 1);
	build(mid + 1, r, rt << 1 | 1);
	pushup(rt, r - l + 1);
}

int query(int L, int R, int l, int r, int rt) {//查询
	if(L <= l && r <= R) {
		return e[rt].sub;
	}
	int ret = 0;
	int mid = (l + r) >> 1;
	if(L <= mid) ret = max(ret, query(L, R, l, mid, rt << 1));
	if(mid < R) ret = max(ret, query(L, R, mid + 1, r, rt << 1 | 1));
	if(e[rt << 1].r < e[rt << 1 | 1].l && mid >= L && mid < R)//关键,看是否取中间的连续上升子序列
		ret = max(ret, min(e[rt << 1].rsub, mid - L + 1) + min(e[rt << 1 | 1].lsub, R - mid));
	return ret;
}

void update(int p, int x, int l, int r, int rt) {//更新
	if(l == r) {
		e[rt].l = e[rt].r = x;
		return;
	}
	int mid = (l + r) >> 1;
	if(p <= mid) update(p, x, l, mid, rt << 1);
	else update(p, x, mid + 1, r, rt << 1 | 1);
	pushup(rt, r - l + 1);
}

int main() {
	int T;
	scanf("%d", &T);
	while(T --) {
		scanf("%d %d", &n, &m);
		build(0, n - 1, 1);
		char op[5];
		int a, b;
		while(m --) {
			scanf("%s %d %d", op, &a, &b);
			if(op[0] == 'Q') {
				printf("%d\n", query(a, b, 0, n - 1, 1));
			}
			else if(op[0] == 'U') {
				update(a, b, 0, n - 1, 1);
			}
		}
	}
	return 0;
}
时间: 2024-10-10 07:05:46

HDU - 3308 - LCIS (线段树 - 区间合并)的相关文章

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

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 题目很好懂,就是单点更新,然后求区间的最长上升子序列. 线段树区间合并问题,注意合并的条件是a[mid + 1] > a[mid],写的细心点就好了. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int MAXN = 1

HDU 3308 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, indicat

HDU 3308 LCIS 线段树 区间更新

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3308 题目描述: 有两种操作, U x y  , 第xth赋值为y .Q x y , 查询区间x-y的最长连续上升子序列的长度L 解题思路: 对于线段树不好的我依然好难.....有太多细节需要注意了....但是这是一道很好的题, 一段区间的L可能从三个地方来, 一种是中间, 一种是以左起点为开头的, 一种是以右起点结尾的, 这样查询的时候就要注意了: 如果两段的中间值是a[m] < a[m+1]

HDU 3308 LCIS (端点更新+区间合并)

刚刚做了两道LCIS,碰到这道线段树,脑抽了似的写 线段树+dp(LCIS),贡献一发TLE. 才想到要区间合并,query函数写了好久.下面有详细注释,参见代码吧~~欢迎点赞,欢迎卖萌~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 #include<cstdio> #inc

HDU 3308 LCIS (线段树&#183;单点更新&#183;区间合并)

题意  给你一个数组  有更新值和查询两种操作  对于每次查询  输出对应区间的最长连续递增子序列的长度 基础的线段树区间合并  线段树维护三个值  对应区间的LCIS长度(lcis)  对应区间以左端点为起点的LCIS长度(lle)  对应区间以右端点为终点的LCIS长度(lri)  然后用val存储数组对应位置的值  当val[mid + 1] > val[mid] 的时候就要进行区间合并操作了 #include <cstdio> #include <algorithm>

hdu 3308 LCIS(线段树)

pid=3308" target="_blank" style="">题目链接:hdu 3308 LCIS 题目大意:给定一个序列,两种操作: Q l r:查询区间l,r中的最长连续递增序列长度 U p x:将位置p上的数改成x 解题思路:线段树上的区间合并,这是在左右子树合并的时候要推断一下是否满足递增就可以. #include <cstdio> #include <cstring> #include <algorit

HDU 5316 Magician(线段树区间合并, 子序列最值 多校2015啊)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5316 Problem Description Fantasy magicians usually gain their ability through one of three usual methods: possessing it as an innate talent, gaining it through study and practice, or receiving it from an

HDU 1540(线段树+区间合并)学习记录

学习了线段树的新姿势,记录一下 参考blog:https://blog.csdn.net/sunyutian1998/article/details/79618316 query的时候m-ql+1和qr-m写成了m-l+1.r-m,wa了几发之后才找到bug 错误样例: 10 1Q 5 wrong answer:5 顺带一提,这个题目可以在set上面二分直接过,不过最主要还是为了练习区间合并 #include<bits/stdc++.h> using namespace std; const

hdu 1540(线段树区间合并)

题目链接:传送门 参考文章:传送门 题意:n个数字初始连在一条线上,有三种操作, D x表示x号被摧毁: R 表示恢复剩下的通路 Q表示查询标号为x所在的串的最长长度. 思路:线段树的区间合并. #include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn = 50500; struct Node{ int l,r; int ls,rs,ms; }cur

HDU 3308 LCIS(最长连续上升子序列)(线段树区间合并)

题意:给你n个整数,有两种操作,U A B把第A个数变成B,Q A B查询区间[A,B]的最长连续上升序列. 思路:还是查询和更新操作,而且也是询问区间中满足条件的连续最长区间 ,所以是线段树区间合并类型的题,通法是开三棵线段树,一个记录此区间内的LCIS的最长长度,一个记录从左边第一个数开始的LCIS长度,另一个记录从右边最后一个数结尾的LCIS长度.然后试图找到父亲与儿子关系维护的递推关系式就好 本题中的递推关系是: 1. 左儿子最右边的值 < 右儿子最左边的值 lmx = (左儿子的lmx