题解:CF2026F Bermart Ice Cream

读完题,我们能很容易想到,可以将所有的操作看作是在一棵状态树上进行。操作一相当于直接把当前商店的指针指向已有的状态;操作二和操作三相当于在当前状态下衍生出一个新的子状态,并在树上连边。这样能够得到一棵状态树,每个商店在任何时刻都对应树上的一个节点。对这棵树进行深度优先遍历,得到每个节点的入栈和出栈时间戳,即可将每一个状态子树映射到一个连续的时间戳区间上。

对于操作二添加的一个物品,如果没有被删除,它会在当前状态及其所有的后代状态中存在,即它的存在范围初始是该状态对应的时间戳区间。

对于操作三,需要删去最早加入且未被删除的物品。为了快速定位被删除的物品,我们可以维护一棵主席树。每个状态在主席树上按照添加顺序记录它所拥有的所有物品。同时记录每个状态已经删除了多少个物品。在执行操作三时,我们只需在父状态的主席树上查询排名为已删除物品数加一的物品,就可以准确找到被删除的物品是哪一次添加的。

找到被删除的物品后,记录该物品在当前执行删除操作的状态节点失效。这意味着该物品在该删除状态及其子树中是不存在的,需要将该删除状态的时间戳区间从该物品的初始存在区间中挖去。由于一个物品可能会在多个不同的分支衍生中被删除,我们可以将所有删除该物品的状态的时间戳区间按左端点排序,然后从初始区间中依次剔除这些子区间,最终得到若干个互不相交的区间,表示该物品真正在商店中存在的状态时间戳集合。

然后,现在问题就转化为了每个物品在若干个时间戳区间内存在,在某些单点进行背包容量有限制的最大价值查询。可以通过线段树分治来解决。

以时间戳序列为底层建立一棵线段树,将每个物品的有效存在区间拆分并覆盖到线段树的对应节点上。然后从根节点开始遍历这棵线段树。由于每次查询的背包容量不超过两千,在向下递归时,可以直接维护一个 \(0/1\) 背包的动态规划数组。每到一个节点,就将该节点上挂载的所有物品作为背包的物品进行转移更新。为了在回溯时撤销操作,考虑到背包数组容量较小,可以在每一层递归前按树的深度复制保存当前的动态规划数组,递归返回时直接利用备份数组覆盖还原。当递归到线段树的叶子节点时,直接用当前维护好的背包数组回答所有对应在该时间戳上的操作四询问即可。

代码有一点丑陋

#include<bits/stdc++.h>
using namespace std;
namespace FastIO {
	const int BUFSIZE = 1 << 20;
	char ibuf[BUFSIZE], obuf[BUFSIZE];
	int ipos = 0, ilen = 0, opos = 0;
	inline char gc() {
		if (ipos == ilen) {
			ilen = fread(ibuf, 1, BUFSIZE, stdin);
			ipos = 0;
			if (!ilen) {
				return EOF;
			}
		}
		return ibuf[ipos++];
	}
	template<typename T>
	inline void read(T &x) {
		x = 0;
		int f = 1;
		char c = gc();
		while (c != '-' && (c < '0' || c > '9')) {
			c = gc();
		}
		if (c == '-') {
			f = -1;
			c = gc();
		}
		while (c >= '0' && c <= '9') {
			x = x * 10 + (c - '0');
			c = gc();
		}
		x *= f;
	}
	inline void write(int x) {
		if (!x) {
			obuf[opos++] = '0';
			obuf[opos++] = '\n';
		} else {
			if (x < 0) {
				obuf[opos++] = '-';
				x = -x;
			}
			char tmp[25];
			int t = 0;
			while (x) {
				tmp[t++] = '0' + x % 10;
				x /= 10;
			}
			while (t) {
				obuf[opos++] = tmp[--t];
			}
			obuf[opos++] = '\n';
		}
		if (opos > BUFSIZE - 100) {
			fwrite(obuf, 1, opos, stdout);
			opos = 0;
		}
	}
	inline void flush() {
		fwrite(obuf, 1, opos, stdout);
		opos = 0;
	}
};
using namespace FastIO;
struct Node {
	int cost, k, tast;
	bool flag;
} a[30005];
int t, tree[1000005], ls[1000005], rs[1000005], root[30005], treeid, dfn[30005], low[30005], dfsid, item[2000005], nxt[2000005], head[120005], trid, dp[2005], f[25][2005], ans[30005], qhead[30005], qnxt[30005], point[30005], w[30005], cnt, version[30005], delhead[30005], delnxt[30005], delpoint[30005], delcnt, ehead[30005], enxt[30005], ecnt;
int change(int p, int l, int r, int x) {
	int rt = ++treeid;
	ls[rt] = ls[p];
	rs[rt] = rs[p];
	tree[rt] = tree[p];
	if (l == r) {
		tree[rt] ++;
		return rt;
	}
	int mid = (l + r) >> 1;
	if (x <= mid) {
		ls[rt] = change(ls[p], l, mid, x);
	} else {
		rs[rt] = change(rs[p], mid + 1, r, x);
	}
	tree[rt] = tree[ls[rt]] + tree[rs[rt]];
	return rt;
}
int getkth(int p, int l, int r, int x) {
	if (l == r) {
		return l;
	}
	int mid = (l + r) >> 1;
	if (x <= tree[ls[p]]) {
		return getkth(ls[p], l, mid, x);
	}
	return getkth(rs[p], mid + 1, r, x - tree[ls[p]]);
}
void dfs(int u) {
	dfn[u] = ++dfsid;
	for (int i = ehead[u]; i; i = enxt[i]) {
		dfs(i);
	}
	low[u] = dfsid;
}
void segchange(int p, int l, int r, int ll, int rr, int itm) {
	if (ll > rr) {
		return;
	}
	if (ll <= l && r <= rr) {
		item[++trid] = itm;
		nxt[trid] = head[p];
		head[p] = trid;
		return;
	}
	int mid = (l + r) >> 1;
	if (ll <= mid) {
		segchange(p * 2, l, mid, ll, rr, itm);
	}
	if (rr > mid) {
		segchange(p * 2 + 1, mid + 1, r, ll, rr, itm);
	}
}
void solve(int p, int l, int r, int dep) {
	memcpy(f[dep], dp, sizeof(dp));
	for (int i = head[p]; i; i = nxt[i]) {
		int u = item[i];
		for (int j = 2000; j >= a[u].cost; j --) {
			if (dp[j - a[u].cost] + a[u].tast > dp[j]) {
				dp[j] = dp[j - a[u].cost] + a[u].tast;
			}
		}
	}
	if (l == r) {
		for (int i = qhead[l]; i; i = qnxt[i]) {
			ans[i] = dp[w[i]];
		}
	} else {
		int mid = (l + r) >> 1;
		solve(p * 2, l, mid, dep + 1);
		solve(p * 2 + 1, mid + 1, r, dep + 1);
	}
	memcpy(dp, f[dep], sizeof(dp));
}
signed main() {
	read(t);
	int num = 1;
	version[1] = 0;
	for (int i = 1; i <= t; i ++) {
		int op, x;
		read(op);
		read(x);
		if (op == 1) {
			version[++num] = version[x];
		} else if (op == 2) {
			int y, z;
			read(y);
			read(z);
			a[i].flag = true;
			a[i].cost = y;
			a[i].tast = z;
			int fa = version[x];
			a[i].k = a[fa].k;
			root[i] = change(root[fa], 1, t, i);
			enxt[i] = ehead[fa];
			ehead[fa] = i;
			version[x] = i;
		} else if (op == 3) {
			int fa = version[x], pos = getkth(root[fa], 1, t, a[fa].k + 1);
			a[i].k = a[fa].k + 1;
			root[i] = root[fa];
			delnxt[++delcnt] = delhead[pos];
			delpoint[delcnt] = i;
			delhead[pos] = delcnt;
			enxt[i] = ehead[fa];
			ehead[fa] = i;
			version[x] = i;
		} else {
			int y;
			read(y);
			point[++cnt] = version[x];
			w[cnt] = y;
		}
	}
	dfs(0);
	int n = dfsid;
	for (int i = 1; i <= cnt; i ++) {
		int pos = dfn[point[i]];
		qnxt[i] = qhead[pos];
		qhead[pos] = i;
	}
	pair<int, int> b[30005];
	for (int i = 1; i <= t; i ++) {
		if (!a[i].flag) {
			continue;
		}
		int numb = 0;
		for (int j = delhead[i]; j; j = delnxt[j]) {
			int to = delpoint[j];
			b[numb++] = {dfn[to], low[to]};
		}
		sort(b, b + numb);
		int l = dfn[i], r = low[i], pos = l;
		for (int j = 0; j < numb; j ++) {
			int ll = b[j].first, rr = b[j].second;
			if (pos < ll) {
				segchange(1, 1, n, pos, ll - 1, i);
			}
			if (rr + 1 > pos) {
				pos = rr + 1;
			}
		}
		if (pos <= r) {
			segchange(1, 1, n, pos, r, i);
		}
	}
	solve(1, 1, n, 0);
	for (int i = 1; i <= cnt; i ++) {
		write(ans[i]);
	}
	flush();
	return 0;
}
posted @ 2026-11-11 15:10  xuyifei0302  阅读(4)  评论(0)    收藏  举报