「IOI2021Day1T1」分糖果

直接用数据结构做,无论是势能线段树或是分块都难以维护。所以考虑数据结构维护对于固定糖果盒,每个时刻的修改情况

传统的做法是每个时刻单独维护一个数据结构,数据结构存有每个糖果盒的信息。这里我们改成每个糖果盒单独维护一个数据结构,数据结构存有每个时刻的信息。

用线段树维护,假设当前只考虑盒子 \(i\) 有关的修改。以时间为下标,记下如果没有取 \(\max,\min\),每个时刻 \(i\) 内有多少颗糖,也即修改量的前缀和。

我们把糖果盒满叫做碰上界,糖果盒空叫做碰下界。初始时,每个盒子都是空的,我们认为这算一次碰下界。如果碰上界后经过若干次修改,还没碰下界就又碰了上界,我们只把后面那次称作碰上界。下界同理。

那么我们用一个图像表示盒子内的糖数就是这样:

\(0\) 就是下界,\(c_i\) 就是上界。折线表示不考虑 \(\max,\min\) 的糖数(为了方便,称 \(i\) 时刻不考虑 \(\max,\min\) 的糖数为 \(s_i\)),横轴为时间轴,纵轴为数量。

如果我们想要知道最后的值,显然只需要知道最后一次碰上/下界的位置。

显然,如果区间内 \(s\) 极差 \(\ge c_i\),则区间内必定包含最后两次碰上/下界。然后就可以找出最后一次,进而求出答案。所以线段树上二分就可以了。

由这题得出的一个经验是,当某些动态数据结构问题直接做难以维护时,可以考虑离线,对每个对象(如本题中的糖果盒)分开考虑,用一些数据结构维护时间轴上每一次修改。

#include "candies.h"
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstdlib>
typedef int INT;
#define int long long

inline int min(const int x, const int y) {return x < y ? x : y;}
inline int max(const int x, const int y) {return x > y ? x : y;}
struct Line {
	int l, r, v, tim;
	inline bool operator < (const Line a) const {return l < a.l;}
} a[200005], b[200005];
inline bool cmp(Line a, Line b) {return a.r < b.r;}

std::vector<INT> ans;
int q;
struct Node {
	int l, r, mi, mx, tag;
} tree[800005];
void build(int O, int L, int R) {
	tree[O].l = L, tree[O].r = R;
	if (L != R) build(O << 1, L, L + R >> 1), build(O << 1 | 1, (L + R >> 1) + 1, R);
}
void pushdown(int O) {
	tree[O << 1].mi += tree[O].tag, tree[O << 1].mx += tree[O].tag;
	tree[O << 1].tag += tree[O].tag;
	tree[O << 1 | 1].mi += tree[O].tag, tree[O << 1 | 1].mx += tree[O].tag;
	tree[O << 1 | 1].tag += tree[O].tag;
	tree[O].tag = 0;
}
void update(int O, int p, int v) {
	if (p <= tree[O].l) {
		tree[O].mi += v, tree[O].mx += v, tree[O].tag += v;
		return;
	}
	pushdown(O);
	int mid = tree[O].l + tree[O].r >> 1;
	if (p <= mid) update(O << 1, p, v);
	update(O << 1 | 1, p, v);
	tree[O].mx = max(tree[O << 1].mx, tree[O << 1 | 1].mx);
	tree[O].mi = min(tree[O << 1].mi, tree[O << 1 | 1].mi);
}
int getsum() {
	int p = 1;
	while (tree[p].l != tree[p].r) pushdown(p), p = p << 1 | 1;
	return tree[p].tag;
}
int query(int O, int mi, int mx, int c) {
	if (tree[O].l == tree[O].r) {
		mi = min(mi, tree[O].tag), mx = max(mx, tree[O].tag);
		if (tree[O].tag == mi) return getsum() + c - mx;
		else return getsum() - mi;
	}
	pushdown(O);
	int mi2 = min(mi, tree[O << 1 | 1].mi), mx2 = max(mx, tree[O << 1 | 1].mx);
	if (mx2 - mi2 >= c) return query(O << 1 | 1, mi, mx, c);
	else return query(O << 1, mi2, mx2, c);
}

std::vector<INT> distribute_candies(std::vector<INT> c, std::vector<INT> l, std::vector<INT> r,
std::vector<INT> v) {
	int n = (int)c.size();
	q = (int)l.size();
	build(1, 0, q);
	for (int i = 1; i <= q; ++ i) a[i] = b[i] = Line{l[i - 1], r[i - 1], v[i - 1], i};
	std::sort(a + 1, a + q + 1);
	std::sort(b + 1, b + q + 1, cmp);
	for (int T = 0, i = 1, j = 1; T < n; ++ T) {
		while (i <= q && a[i].l <= T) update(1, a[i].tim, a[i].v), ++ i;
		while (j <= q && b[j].r < T) update(1, b[j].tim, -b[j].v), ++ j;
		if (tree[1].mx - tree[1].mi <= c[T]) ans.push_back(getsum() - tree[1].mi);
		else ans.push_back(query(1, 1e18, -1e18, c[T]));
	}
	return ans;
}
posted @ 2022-01-30 16:41  zqs2020  阅读(124)  评论(0)    收藏  举报