HDU5306 Gorgeous Sequence

传送门


题面言简意赅,就不翻译了。

这个似乎就是Segment Tree Beats了,参考自2016年国家集训队论文,吉如一《区间最值操作与历史最值问题》。但是有关时间复杂度的证明我没有看懂,这里只是记录一下实现方法。


我们需要维护区间和、最大值\(Max1\)、严格次大值\(Max2\),以及最大值的出现次数\(num\)

对于一次修改操作,我们要让区间\([L,R]\)\(x\)\(\textrm{min}\)。具体做法是先正常在线段树上二分,找到这个区间对应的节点。当找到这个节点时,要分三种情况讨论:

  1. \(Max1 \leqslant x\)时,显然修改不会产生影响,直接返回。
  2. \(Max2 < x < Max1\)时,这次修改只会影响到最大值,遂把区间和减去\(num * (Max1 - x)\),再把最大值改成\(x\).
  3. \(x \leqslant Max2\)时,继续递归到子区间。

综上,当且仅当区间满足条件2的时候,才会被修改。

而对于标记的下传,会发现我们并没有维护lazy标记,而是选择直接下传\(Max1\):当子区间和当前区间的\(Max1\)也满足上述条件2时,我们才会更新子区间。实际上,对于当前区间\(now\)的最大值和子区间的关系,只会出现上述1,2两种情况。因为在没修改时,\(now\)的最大、次大值肯定分别大于等于子区间的最大、次大值;当修改的时候,只有满足条件2才会修改,因此次大值并没有改变,也就是说,仍满足\(now\)的次大值大于等于子区间的次大值,所以下传的时候只会出现条件1,2.

而至于为什么只用下传\(Max1\),我是这么想的:因为在区间修改的时候,只会改最大值,而\(now\)和子区间的最大值是单调的:即如果\(x > now.Max1\),那么必有\(x > son.Max1\),所以子区间的最大值是否会被修改的前提是\(now\)的最大值一定被修改了,那么对于子区间来说,用\(now.Max1\)代替\(x\),一定也能考虑到所有修改的情况。所以我们下传的时候传最大值即可,而不用单独维护\(lazy\)标记。


时间复杂度是\(O(m \log n)\)的。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<queue>
#include<assert.h>
#include<ctime>
using namespace std;
#define enter puts("") 
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
#define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 1e6 + 5;
In ll read()
{
	ll ans = 0;
	char ch = getchar(), las = ' ';
	while(!isdigit(ch)) las = ch, ch = getchar();
	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
	if(las == '-') ans = -ans;
	return ans;
}
In void write(ll x)
{
	if(x < 0) x = -x, putchar('-');
	if(x >= 10) write(x / 10);
	putchar(x % 10 + '0');
}
In void MYFILE()
{
#ifndef mrclr
	freopen(".in", "r", stdin);
	freopen(".out", "w", stdout);
#endif
}

int n, m;
struct Tree
{
	int l, r;
	int Max1, Max2, num;
	ll sum;
	In Tree operator + (const Tree& oth)const
	{
		Tree ret; ret.l = l, ret.r = oth.r;
		ret.sum = sum + oth.sum;
		ret.Max1 = max(Max1, oth.Max1);
		if(Max1 > oth.Max1)	//看了两种写法,还是分三种情况讨论比较好写,还不容易写错 
		{
			ret.Max2 = max(Max2, oth.Max1);
			ret.num = num;
		}
		else if(Max1 < oth.Max1)
		{
			ret.Max2 = max(Max1, oth.Max2);
			ret.num = oth.num;
		}
		else
		{
			ret.Max2 = max(Max2, oth.Max2);
			ret.num = num + oth.num;
		}
		return ret;
	}
}t[maxn << 2];
In void build(int L, int R, int now)
{
	t[now].l = L, t[now].r = R;
	if(L == R)
	{
		t[now].Max1 = t[now].sum = read();
		t[now].Max2 = -1, t[now].num = 1;
		return;
	}
	int mid = (L + R) >> 1;
	build(L, mid, now << 1), build(mid + 1, R, now << 1 | 1);
	t[now] = t[now << 1] + t[now << 1 | 1];
}
In void Change(int now, int d)
{
	t[now].sum -= 1LL * t[now].num * (t[now].Max1 - d);
	t[now].Max1 = d;
}
In void pushdown(int now)
{
	int tp = t[now].Max1;
	if(t[now << 1].Max2 < tp && tp < t[now << 1].Max1) Change(now << 1, tp);
	if(t[now << 1 | 1].Max2 < tp && tp < t[now << 1 | 1].Max1) Change(now << 1 | 1, tp);
}
In void update(int L, int R, int now, int d)
{
	if(t[now].Max1 <= d) return;
	if(t[now].l == L && t[now].r == R && t[now].Max2 < d) return (void)Change(now, d);
	pushdown(now);
	int mid = (t[now].l + t[now].r) >> 1;
	if(R <= mid) update(L, R, now << 1, d);
	else if(L > mid) update(L, R, now << 1 | 1, d);
	else update(L, mid, now << 1, d), update(mid + 1, R, now << 1 | 1, d);
	t[now] = t[now << 1] + t[now << 1 | 1];
}
In int queryMax(int L, int R, int now)
{
	if(t[now].l == L && t[now].r == R) return t[now].Max1;
	pushdown(now);
	int mid = (t[now].l + t[now].r) >> 1;
	if(R <= mid) return queryMax(L, R, now << 1);
	else if(L > mid) return queryMax(L, R, now << 1 | 1);
	else return max(queryMax(L, mid, now << 1), queryMax(mid + 1, R, now << 1 | 1));
}
In ll querySum(int L, int R, int now)
{
	if(t[now].l == L && t[now].r == R) return t[now].sum;
	pushdown(now);
	int mid = (t[now].l + t[now].r) >> 1;
	if(R <= mid) return querySum(L, R, now << 1);
	else if(L > mid) return querySum(L, R, now << 1 | 1);
	else return querySum(L, mid, now << 1) + querySum(mid + 1, R, now << 1 | 1);
}

int main()
{
//	MYFILE();
	int T = read();
	while(T--)
	{
		n = read(), m = read();
		build(1, n, 1);
		for(int i = 1; i <= m; ++i)
		{
			int op = read(), L = read(), R = read();
			if(op == 0) update(L, R, 1, read());
			else if(op == 1) write(queryMax(L, R, 1)), enter;
			else write(querySum(L, R, 1)), enter;
		}
	}
	return 0;
}
posted @ 2021-09-05 17:41  mrclr  阅读(82)  评论(0编辑  收藏  举报