hdu-5306(区间最值+线段树)

发布时间 2023-04-05 17:30:30作者: 魏老6

hdu

Gorgeous Sequence

HDU - 5306

题意:

给定一个长度为n的区间,做m次操作,三种操作

  1. 对于序列[L,R]区间中的每个ai,用min(ai,x)替换。
  2. 打印序列[L,R]区间的最大值
  3. 打印序列[L,R]区间和

因为区间和与区间最值无关,所以无法直接用简单的标记处理。

区间最值与区间和如何扯上关系呢?我们通树状数组维护四个信息,分别是区间和sum,最大值mx,严格次大值se,最大值个数t

当我们用x来进行区间修改时:

(1) 当mx<=x时,这次修改不影响。

(2)当se<x < mx时,此时sum需要更新,sum = sum - t(mx- x), 以及mx = x //自己想想为什么

(3)当se>=x时,无法更新,我们递归他的左右结点

#define _CRT_SECURE_NO_WARNINGS 1
#include<algorithm>
#include<fstream>
#include<iostream>
#include<cstdio>
#include<deque>
#include<string>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<unordered_map>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 310000
#define N 1000010
#define M 10007
#define endl '\n'
#define exp 1e-8
#define lc p << 1
#define rc p << 1|1
#define lowbit(x) ((x)&-(x))
const double pi = acos(-1.0);
typedef long long LL;
typedef unsigned long long ULL;
inline ULL read() {
	ULL x = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch>'9') {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 1) + (x << 3) + (ch ^ 48);
		ch = getchar();
	}
	return x * f;
}
void print(ULL x) {
	if (x > 9)print(x / 10);
	putchar(x % 10 ^ 48);
}
struct tree
{
	LL l, r,sum,mx,se,num;  //sum区间和,mx最大值,se次大值,num最大值个数,不用在写标记的原因:mx可以充当标记的作用
}tr[N*4];
LL arr[N], t, n, m;
void pushup(int p)  //pushup()函数的改写
{
	tr[p].mx = max(tr[lc].mx, tr[rc].mx);
	tr[p].sum = tr[lc].sum + tr[rc].sum;
	if (tr[lc].mx == tr[rc].mx)   //当左节点和右结点最大值相同时
	{
		tr[p].se = max(tr[lc].se, tr[rc].se);  //次大值从左节点的次大值和右节点的次大值寻找最大
		tr[p].num = tr[lc].num + tr[rc].num;   //最大值相等,最大值个数自然是相加
	}
	else
	{
		tr[p].se = max(tr[lc].se, tr[rc].se);        
		tr[p].se = max(tr[p].se, min(tr[lc].mx, tr[rc].mx));   //为什么可以求得最大值,可以自己模拟一下
		tr[p].num = tr[lc].mx > tr[rc].mx ? tr[lc].num : tr[rc].num;  //取了谁的最大值,那么最大值个数也应该取谁的
	}
}
void addtag(int p, int k)   
{
	if (tr[p].mx <= k)return;
	tr[p].sum -= tr[p].num * (tr[p].mx - k);
	tr[p].mx = k;
}
void pushdown(LL p)
{
	addtag(lc, tr[p].mx);  //mx充当了标记的作用
	addtag(rc,tr[p].mx);
}
void build(LL p, LL l, LL r)
{                                                                                                                                                        
	tr[p] = { l,r,arr[l],arr[l],-1,1};
	if (l == r)return;
	LL m = l + r >> 1;
	build(lc, l, m);
	build(rc, m + 1, r);
	pushup(p);
}
void update(LL p, LL x, LL y, LL k)
{
	if (k >=tr[p].mx)return;  //第一种情况
	if (x <= tr[p].l && tr[p].r <= y) //第二种情况
	{
		if (k>tr[p].se)
		{
			addtag(p, k);
			return;
		}
	}
	pushdown(p);
	LL m = tr[p].l + tr[p].r >> 1;
	if (x <= m)update(lc, x, y, k);
	if (y > m)update(rc, x, y, k);
	pushup(p);
}
LL query1(LL p, LL x, LL y)  //求区间最大值
{
	if (x <= tr[p].l && tr[p].r <= y)
	{
		return tr[p].mx;
	}
	pushdown(p);
	LL ans = 0;
	LL m = tr[p].l + tr[p].r >> 1;
	if (x <= m)ans = max(ans,query1(lc, x, y));
	if (y > m)ans = max( ans,query1(rc, x, y));
	return ans;
}
LL query2(LL p, LL x, LL y)  //求区间和
{
	if (x <= tr[p].l && tr[p].r <= y)
	{
		return tr[p].sum;
	}
	pushdown(p);
	LL ans = 0;
	LL m = tr[p].l + tr[p].r >> 1;
	if (x <= m)ans += query2(lc, x, y);
	if (y > m)ans += query2(rc, x, y);
	return ans;
}
int main()
{
	t = read();
	while (t--)
	{
		n = read(), m = read();
		for (int i = 1; i <= n; i++)
		{
			arr[i] = read();
		}
		build(1, 1, n);
		while (m--)
		{
			LL a, x, y, k;
			a = read(), x = read(), y = read();
			if (a == 0)
			{
				k = read();
				update(1, x, y, k);
			}
			else if (a == 1)
			{
				printf("%lld\n", query1(1, x, y));
			}
			else
			{
				printf("%lld\n", query2(1, x, y));
			}
		}
	}
	return 0;
}