【LOJ #534】「LibreOJ Round #6」花团

Description

LOJ #534

物品集合 (S) 初始为空,按时间递增顺序依次给出 (q) 次操作,操作如下:

  • 1 v w e:表示在 (S) 中加入一个体积为 (v) 价值为 (w) 的物品,第 (e) 次操作结束之后移除该物品。

  • 2 v:表示询问。你需要回答:

    1. 当前 (S) 是否存在一个子集使得子集中物品体积和为 (v)
    2. 当前 (S) 的所有物品体积和为 (v) 的子集中,价值和最大是多少(空集的价值和为 0)。

本题强制在线。

数据范围:(1 leq q leq 15000)(1 leq v_i leq 15000)(0 leq w_i leq 15000)

Solution

如果没有强制在线的话,可以考虑线段树分治,大家应该都会。

对时间轴建一棵线段树。

就是对于每个插入操作,将物品信息挂在线段树上覆盖插入区间的 (mathcal{O}(log q)) 个节点上的 vector 里。

然后遍历整棵树,若当前到达了某个点,就对该点的 vector 上挂着的所有物品跑一遍 (0/1) 背包。然后递归左右儿子。若当前到达了叶节点,是询问的话就直接回答询问即可。回溯时要撤销物品影响。

时间复杂度 (mathcal{O}(qv log q))说实话我也不知道这个复杂度是怎么跑过去的。
值得注意的是,如果直接开栈储存 dp 数组的修改信息时,空间复杂度会达到 (mathcal{O}(qv log q))

事实上,我们可以多开一维,记录 dp 数组所处理的节点的深度信息。
例如:f[dep][] 就表示处理到第 ( ext{dep}) 层的某一个节点时,有关该节点的 dp 数组。

在跑 (0/1) 背包的时候,就直接在 f[dep][] 上进行 dp。
在往左右儿子递归的时候,用 (mathcal{O}(v)) 的时间将 f[dep][] 的信息继承给 f[dep + 1][]
可以归纳证明该做法的正确性,这里留给读者。

这样做,空间复杂度就降到了 (mathcal{O}(v log q))


如果一定要强制在线的话,还是可以考虑线段树分治。

注意到本题有一个特殊条件:给出物品时同时给出了移除时间。
并且在线段树上遍历的过程,时间是从左到右 (1 o q) 的顺序进行访问的。

这意味这我们可以在没有读入 (q) 个操作时,直接开始遍历过程。

当到达了叶节点时,将操作读进来。如果是插入操作,则将物品信息挂在线段树上覆盖该插入区间的 (mathcal{O}(log q)) 个节点上的 vector 里,显然这样插入,这些被操作的节点一定不会出现曾经被遍历过的节点,也就不影响到曾经的操作了。如果是询问操作,则直接回答询问即可,记得更新 ( ext{lastans})

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector> 

using namespace std;

inline int read() {
	int x = 0, f = 1; char s = getchar();
	while (s < '0' || s > '9') { if (s == '-') f = -f; s = getchar(); }
	while (s >= '0' && s <= '9') { x = x * 10 + s - '0'; s = getchar(); }
	return x * f;
}

const int N = 100100;
const int INF = 1e9;

int Q, m, T;

int lastans;

vector< pair<int, int> > art[N * 4];

void insert(int p, int l, int r, int s, int e, pair<int, int> x) {
	if (s <= l && r <= e) {
		art[p].push_back(x);
		return;
	}
	int mid = (l + r) >> 1;
	if (s <= mid)
		insert(p * 2, l, mid, s, e, x);
	if (mid < e)
		insert(p * 2 + 1, mid + 1, r, s, e, x);
}

int f[20][N];

void solve(int p, int l, int r, int dep) {
	for (int j = 0; j < (int)art[p].size(); j ++) {
		pair<int, int> x = art[p][j];
		int w = x.first, v = x.second;
		for (int i = m; i >= w; i --)
			f[dep][i] = max(f[dep][i], f[dep][i - w] + v);
	}

	if (l == r) {
		int opt = read();
		int d = lastans * T;

		switch (opt) {
			case 1: {
				int w = read() - d, v = read() - d, e = read() - d;
				insert(1, 1, Q, l + 1, e, make_pair(w, v));
				break;
			}

			case 2: {
				int v = read() - d;
				int X, Y;

				if (f[dep][v] > -INF) X = 1, Y = f[dep][v];
				else X = Y = 0;

				printf("%d %d
", X, Y);
				lastans = X ^ Y;

				break;
			}
		}
	} else {
		int mid = (l + r) >> 1;

		for (int i = 0; i <= m; i ++) f[dep + 1][i] = f[dep][i];
		solve(p * 2, l, mid, dep + 1);

		for (int i = 0; i <= m; i ++) f[dep + 1][i] = f[dep][i];
		solve(p * 2 + 1, mid + 1, r, dep + 1);
	}
}

int main() {
	Q = read(), m = read(), T = read();

	f[0][0] = 0;
	for (int i = 1; i <= m; i ++)
		f[0][i] = -INF * 2;

	solve(1, 1, Q, 0);

	return 0;
}
keep the love forever.
原文地址:https://www.cnblogs.com/cjtcalc/p/14696077.html