SEERC2021 Gym103438B New Queries On Segment Deluxe 可持久化线段树

Dashboard – 2021 ICPC Southeastern Europe Regional Contest – Codeforces

题意:有 $k$ 个长为 $n$ 的序列,并且定义序列 $b_i$ 表示把 $k$ 个序列的对应位置加起来之后得到的序列。

现在每次可以把某一行的某个区间全部加上一个数,或者某一行的某个区间全部赋值成一个数,或者询问某个区间中 $b_i$ 的最小值,要求可持久化。

$k \le 4$


本题核心思想实际上只有一个,就是把区间赋值操作看成区间清空再加上对应的数,这样就转化成了区间加。

至于区间清空实际上是不需要支持的,可以维护一个这一行始终全 $0$ 的线段树,需要清空的时候就接上全 $0$ 线段树的对应结点。题目已经要求可持久化了,所以直接开 $2^k$ 个线段树来维护每行是否全 $0$ 就行了。

区间加区间查询最值是可以用标记永久化实现的,但是要注意一个问题:因为存在把一棵树接到另一棵树上的操作,所以在接上去的时候要把标记减掉两棵树祖先标记总和的差值,这样才能让最后累加之后的标记等于新树上应该等于的值。

#include <bits/stdc++.h>

using namespace std;

constexpr int maxn = 250005, maxm = (maxn * 2 + 20000 * 19 * 2) * 17;

int lc[maxm], rc[maxm], seg_cnt;
long long val[maxm], tag[maxm];

int copied(int x) {
	int o = ++seg_cnt;

	lc[o] = lc[x];
	rc[o] = rc[x];
	val[o] = val[x];
	tag[o] = tag[x];

	return o;
}

int a[maxn][4];

int root[20005][16], p;

void build(int l, int r, int &o, int mask) {
	o = ++seg_cnt;
	
	if (l == r) {
		for (int i = 0; i < p; i++)
			if (mask >> i & 1)
				val[o] += a[l][i];
		
		return;
	}

	int mid = (l + r) / 2;

	build(l, mid, lc[o], mask);
	build(mid + 1, r, rc[o], mask);

	val[o] = min(val[lc[o]], val[rc[o]])/* + tag[o]*/;
}

int s, t, d;

void modify_add(int l, int r, int &o, int u) {
	o = copied(u);

	if (s <= l && t >= r) {
		val[o] += d;
		tag[o] += d;
		
		return;
	}

	int mid = (l + r) / 2;

	if (s <= mid)
		modify_add(l, mid, lc[o], lc[u]);
	if (t > mid)
		modify_add(mid + 1, r, rc[o], rc[u]);
	
	val[o] = min(val[lc[o]], val[rc[o]]) + tag[o];
}

void modify_set(int l, int r, int &o, int u, int v, long long tmp = 0) {
	if (s <= l && t >= r) {
		o = copied(v);

		val[o] += d - tmp;
		tag[o] += d - tmp;

		return;
	}

	tmp += tag[u] - tag[v];
	o = copied(u);
	int mid = (l + r) / 2;

	if (s <= mid)
		modify_set(l, mid, lc[o], lc[u], lc[v], tmp);
	if (t > mid)
		modify_set(mid + 1, r, rc[o], rc[u], rc[v], tmp);
	
	val[o] = min(val[lc[o]], val[rc[o]]) + tag[o];
}

long long query(int l, int r, int o) {
	if (s <= l && t >= r)
		return val[o];
	
	int mid = (l + r) / 2;
	long long ans = 0x3f3f3f3f3f3f3f3fll;

	if (s <= mid)
		ans = min(ans, query(l, mid, lc[o]));
	if (t > mid)
		ans = min(ans, query(mid + 1, r, rc[o]));
	
	return ans + tag[o];
}

int main() {

	int n, m;
	scanf("%d%d%d", &p, &n, &m);

	for (int k = 0; k < p; k++)
		for (int i = 1; i <= n; i++)
			scanf("%d", &a[i][k]);
	
	for (int mask = 0; mask < (1 << p); mask++)
		build(1, n, root[0][mask], mask);

	for (int i = 1; i <= m; i++) {
		int op, v;
		scanf("%d%d", &op, &v);

		if (op == 1) {
			int k;
			scanf("%d%d%d%d", &k, &s, &t, &d);
			k--;

			for (int mask = 0; mask < (1 << p); mask++) {
				if (mask >> k & 1)
					modify_add(1, n, root[i][mask], root[v][mask]);
				else
					root[i][mask] = root[v][mask];
			}
		}
		else if (op == 2) {
			int k;
			scanf("%d%d%d%d", &k, &s, &t, &d);
			k--;

			for (int mask = 0; mask < (1 << p); mask++) {
				if (mask >> k & 1)
					modify_set(1, n, root[i][mask], root[v][mask], root[v][mask ^ (1 << k)]);
				else
					root[i][mask] = root[v][mask];
			}

		}
		else if (op == 3) {
			scanf("%d%d", &s, &t);

			printf("%d\n", (int)query(1, n, root[v][(1 << p) - 1]));
		}
	}

	return 0;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇