hdu-5306(区间最值+线段树)
hdu
Gorgeous Sequence
题意:
给定一个长度为n的区间,做m次操作,三种操作
- 对于序列[L,R]区间中的每个ai,用min(ai,x)替换。
- 打印序列[L,R]区间的最大值
- 打印序列[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;
}