[UOJ 25] [IOI 2014] Wall 【线段树】

题目链接:UOJ - 25

题目分析

每个操作就是将被操作的数限制在一个区间,比如 Set_Max(5) 就是将被操作的数限定在了 [5, INF] 的区间里。

这些操作是可加的,但是必须按照顺序,不满足交换律。

对每个节点维护两个标记 Min_Tag[x], Max_Tag[x] ,表示这个节点的数被限制在了 [Max_Tag, Min_Tag] 的区间内。

最终的答案应该是 gmax(Max_Tag[x], gmin(MinTag[x], Val[x]))。其中 Val[x] 是 x 这个位置的初值。

对某一个节点进行 Set_Max(Num) 时,就是进行这样的操作: Max_Tag[x] = gmax(Max_Tag[x], Num); Min_Tag[x] = gmax(Min_Tag[x], Num);

对某一个节点进行 Set_Max(Num) 时,就是进行这样的操作: Max_Tag[x] = gmin(Max_Tag[x], Num); Min_Tag[x] = gmin(Min_Tag[x], Num);

PushDown(x) 的时候就是用 x 的 Max_tag, Min_Tag 对 x 的孩子进行操作。

这样维护两个标记,最后取答案的时候再用 gmax(Max_Tag[x], gmin(MinTag[x], Val[x])) 取答案就行了,不过这道题中初值都是0。

代码

#include "wall.h"

const int MaxN = 2000000 + 5, MaxH = 100000 + 5;

int Min_Tag[MaxN * 4], Max_Tag[MaxN * 4];

void Build(int x, int s, int t)
{
	if (s == t)
	{
		Min_Tag[x] = MaxH;
		Max_Tag[x] = -MaxH;
		return;
	}
	Min_Tag[x] = MaxH;
	Max_Tag[x] = -MaxH;
	int m = (s + t) >> 1;
	Build(x << 1, s, m);
	Build(x << 1 | 1, m + 1, t);
}

inline int gmin(int a, int b) {return a < b ? a : b;}
inline int gmax(int a, int b) {return a > b ? a : b;} 

inline void Paint_Max(int x, int Num)
{
	Max_Tag[x] = gmax(Max_Tag[x], Num);
	Min_Tag[x] = gmax(Min_Tag[x], Num);
}

inline void Paint_Min(int x, int Num)
{
	Max_Tag[x] = gmin(Max_Tag[x], Num);
	Min_Tag[x] = gmin(Min_Tag[x], Num);
}

inline void PushDown(int x)
{
	Paint_Max(x << 1, Max_Tag[x]);
	Paint_Max(x << 1 | 1, Max_Tag[x]);
	Max_Tag[x] = -MaxH;
	Paint_Min(x << 1, Min_Tag[x]);
	Paint_Min(x << 1 | 1, Min_Tag[x]);
	Min_Tag[x] = MaxH;
}

void Set_Max(int x, int s, int t, int l, int r, int Num)
{
	if (l <= s && r >= t)
	{
		Paint_Max(x, Num);
		return;
	}
	PushDown(x);
	int m = (s + t) >> 1;
	if (l <= m) Set_Max(x << 1, s, m, l, r, Num);
	if (r >= m + 1) Set_Max(x << 1 | 1, m + 1, t, l, r, Num);
}

void Set_Min(int x, int s, int t, int l, int r, int Num)
{
	if (l <= s && r >= t)
	{
		Paint_Min(x, Num);
		return;
	}
	PushDown(x);
	int m = (s + t) >> 1;
	if (l <= m) Set_Min(x << 1, s, m, l, r, Num);
	if (r >= m + 1) Set_Min(x << 1 | 1, m + 1, t, l, r, Num);
}

void Get_Ans(int x, int s, int t, int *&P)
{
	if (s == t)
	{
		*P++ = gmax(Max_Tag[x], gmin(0, Min_Tag[x]));
		return;
	}
	PushDown(x);
	int m = (s + t) >> 1;
	Get_Ans(x << 1, s, m, P);
	Get_Ans(x << 1 | 1, m + 1, t, P);
}

void buildWall(int n, int k, int op[], int left[], int right[], int height[], int finalHeight[])
{
	Build(1, 1, n);
	for (int i = 0; i < k; ++i)
	{
		if (op[i] == 1) Set_Max(1, 1, n, left[i] + 1, right[i] + 1, height[i]);
		else Set_Min(1, 1, n, left[i] + 1, right[i] + 1, height[i]);
	}
	int *P = finalHeight;
	Get_Ans(1, 1, n, P);
	return;
}

  

时间: 2024-11-05 21:48:34

[UOJ 25] [IOI 2014] Wall 【线段树】的相关文章

[UOJ #222][NOI2016]区间(线段树)

Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri. 对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度.区间 [li,ri] 的长度定义为 ri−li,即等于它的右端点的值减去左端点的值 求所有合法方案中最小的花费.如果不存在合法的方案,输出 −1. S

10.25算法训练——裸线段树

题目大意:对N(1<=N<=50000)个数进行连续进行M(1<=M<=200000)次询问:问1-N之间任意连续区间最大值和最小值之差. 之前学过线段树,学的是模版题,求解的问题是在一段区间内任意加减,然后再询问任意一段之区间的和. 这次的问题和之前学的模版题相同之处是:查询的是一段连续区间的信息. 不同之处在于:区间求和问题需要在线段树的每个结点记录其左儿子和右儿子所有结点之和.也就是说每个结点的信息是一个数. 所以之后的查询操作,也是不断去访问线段树的结点,将这些结点上的数加

HDU 4391 Paint The Wall 线段树(水

题意: 给定n长的数组,m个操作 下面是每个点的颜色 下面m个操纵: 1 l r col 染色 2 l r col 询问区间内为col颜色的点数 == 就是普通的操作+区间内最大最小颜色数的优化,感觉很不科学... ==感觉可以卡掉这种写法..反正就是不科学嘛 #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <vector>

Just a Hook 线段树 区间更新

Just a Hook In the game of DotA, Pudge's meat hook is actually the most horrible thing for most of the heroes. The hook is made up of several consecutive metallic sticks which are of the same length. Now Pudge wants to do some operations on the hook.

hdu-4893-Wow! Such Sequence!-线段树【2014多校第三场-J】

题意:一个初始为0的数组,支持三种操作:1.向第k个数添加d,(|d| < 2^31);2.把[l, r]区间内的数字都换成与它最相近的Fibonacci数;3.询问[l, r]区间的和. 思路:初始化Fibonacci数组,longlong 类型内90个就够用了. 线段树区间查询,用lazy标记, sgt[]记录线段树各个节点的区间和, fib_num_sum[]记录与各个叶子节点当前值最接近的Fibonacci数,传递到区间fib_num_sum[]就是区间Fibonacci数的和. 操作1

HDU 4902 Nice boat 2014杭电多校训练赛第四场F题(线段树区间更新)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4902 解题报告:输入一个序列,然后有q次操作,操作有两种,第一种是把区间 (l,r) 变成x,第二种是把区间 (l,r) 中大于x的数跟 x 做gcd操作. 线段树区间更新的题目,每个节点保存一个最大和最小值,当该节点的最大值和最小值相等的时候表示这个区间所有的数字都是相同的,可以直接对这个区间进行1或2操作, 进行1操作时,当还没有到达要操作的区间但已经出现了节点的最大值跟最小值相等的情况时,说明

2014 summer 知识点总结1之线段树

HDU 1166 [题意]: n个阵营一字排开,每个初始有a[i]个人.现有两种操作: Q a b 查询[a,b]之间总人数并输出 A/S a b 在a号位添加/删除b个人 [分析]:最基本的单点更新和区间查询,维护节点信息sum[o] [代码]: 1 #include <iostream> 2 #include <string.h> 3 #include <stdio.h> 4 5 using namespace std; 6 7 int numv[50005<

2014 Super Training #7 E Calculate the Function --矩阵+线段树

原题:ZOJ 3772 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3772 这题算是长见识了,还从没坐过矩阵+线段树的题目呢,不要以为矩阵就一定配合快速幂来解递推式的哦. 由F(x)=F(x-1)+F(x-2)*A[x],转化为矩阵乘法:  ===> 所以维护一颗线段树,线段树的每个结点保存一个矩阵,叶子节点为: a[0][0] = a[1][0] = 1, a[0][1] = Ax, a[1][1] = 0的形式

HDU_4893 2014多校三 线段树

给定一个初始都为0的序列,有三种操作,前两种比较正常,一个是对某个位置的数add k,另一个是query区间和.然后比较麻烦的是第三个操作,把某个区间里面的每个值改成离它最近的Fibonacci数,如果存在左右两个离它近的,优先取左边数值小的 一看到前两个操作马上就想上手敲树状数组,后来看到第三个就有点傻眼了,思维当时一直停留在怎么快速改值..但忽略了题目本身要求什么,只有操作2才是输出,也就是只要求区间和值而且,其他两个都是操作而已,在聪哥的提醒下,知道对线段树开两个值记录+一个懒惰标记,一个