题解:QOJ8283 Game of Votes

\(u\) 被淘汰时的票数为 \(r_u\)。淘汰一个人只会使父亲的票数减少,因此淘汰的顺序就是所有点按 \((r_u,u)\) 从大到小排序。

对于叶子节点,\(r_u=a_u\)。考虑如何由儿子节点的 \(r_v\) 推出 \(r_u\)。不妨将儿子节点按 \((r_v,v)\) 从小到大排序得到 \(v_1,\cdots,v_k\)。显然 \(u\) 淘汰时剩余的儿子是一段前缀 \(v_{1\sim i}\)。设 \(pre_i=\sum_{j=1}^ib_{v_j}\),由于保证了 \(u<v_i\)\(i\) 合法的条件为 \(a_u+pre_i>r_i\)。可以想到我们要取的是最大的合法的 \(i\),并令 \(r_u=a_u+pre_i\)。这样我们就能 \(\mathcal{O}(n)\) 解决静态问题。

由于要支持修改,尝试刻画成更好维护的形式。考虑对每个点 \(u\) 开一棵以 \((r_v,v)\) 为关键字的平衡树,我们在节点上维护子树内的 \(b_v\) 之和与 \(\min\{r_v-pre_v\}\),这里 \(pre_v\) 是只考虑子树内点的前缀和。合并是容易的,查询时在平衡树上二分即可。

对于树上的带修问题,套路地考虑把重儿子分离出来考虑。设 \(G_u\) 为只考虑轻儿子时 \(r_u\) 的值,\(H_u\) 为强制加入重儿子时 \(r_u\) 的值。那么 \(r_u\) 的值可以写成关于 \(r_{hson_u}\) 的分段函数:

\[F(r_{hson_u})=\begin{cases} H_u &\text{if }r_{hson_u}<H_u\\ G_u &\text{otherwise} \end{cases} \]

只需求出 \(u\) 到所在重链链底的函数复合即可查询 \(r_u\),容易使用线段树在 DFS 序上维护区间函数复合。

修改时一直跳重链链顶,求出链顶的 \(r_u\) 后在父亲的平衡树上修改并维护信息即可。

时间复杂度为 \(\mathcal{O}((n+q)\log^2{n})\)

主要代码
int n, q, fa[MAXN], a[MAXN], b[MAXN], rt[MAXN];
int stmp, dfn[MAXN], rdfn[MAXN];
int dep[MAXN], sz[MAXN], hson[MAXN], top[MAXN], bot[MAXN];
vector<int> T[MAXN];
ll r[MAXN], G[MAXN], H[MAXN];
mt19937_64 gen(random_device{}());

struct Treap {
#define ls(p) (nd[p].ls)
#define rs(p) (nd[p].rs)
	int tot, top, stk[MAXN];

	struct Node {
		int ls, rs;
		ll w, sum, mn;
		pair<ll, int> val;
		ull sd;
	} nd[MAXN];

	void pushUp(int p) {
		nd[p].sum = nd[ls(p)].sum + nd[p].w + nd[rs(p)].sum;
		nd[p].mn = min({
			nd[ls(p)].mn,
			nd[p].val.first - nd[ls(p)].sum - nd[p].w,
			nd[rs(p)].mn - nd[ls(p)].sum - nd[p].w
		});
	}

	int create(ll w, pair<ll, int> v) {
		int p = top ? stk[top--] : ++tot;
		nd[p] = {0, 0, w, w, v.first - w, v, gen()};
		return p;
	}

	void split(int p, pair<ll, int> v, int &x, int &y) {
		if (!p) {
			x = y = 0;
			return;
		}
		if (nd[p].val <= v) split(rs(p), v, rs(x = p), y);
		else split(ls(p), v, x, ls(y = p));
		pushUp(p);
	}

	int merge(int x, int y) {
		if (!x || !y) return x | y;
		if (nd[x].sd > nd[y].sd) {
			rs(x) = merge(rs(x), y);
			pushUp(x);
			return x;
		} else {
			ls(y) = merge(x, ls(y));
			pushUp(y);
			return y;
		}
	}

	void ins(int &p, int x) {
		auto v = nd[x].val;
		if (!p || nd[p].sd < nd[x].sd) {
			split(p, v, ls(x), rs(x));
			pushUp(p = x);
			return;
		}
		ins(v >= nd[p].val ? rs(p) : ls(p), x);
		pushUp(p);
	}

	void del(int &p, pair<ll, int> v) {
		if (nd[p].val == v) {
			stk[++top] = p;
			p = merge(ls(p), rs(p));
			return;
		}
		del(v >= nd[p].val ? rs(p) : ls(p), v);
		pushUp(p);
	}

	ll find(int p, ll v, ll pre) {
		if (nd[p].mn - pre >= v) return 0;
		if (rs(p) && nd[rs(p)].mn - pre - nd[ls(p)].sum - nd[p].w < v) return find(rs(p), v, pre + nd[ls(p)].sum + nd[p].w);
		else if (nd[p].val.first - pre - nd[ls(p)].sum - nd[p].w < v) return pre + nd[ls(p)].sum + nd[p].w;
		else return find(ls(p), v, pre);
	}
#undef ls
#undef rs
} tr;

struct SegTree {
#define ls(p) (p << 1)
#define rs(p) (p << 1 | 1)
	struct Func {
		ll c1, c2, lim;

		ll calc(ll x) const {
			return x < lim ? c1 : c2;
		}

		friend Func operator+(const Func &lhs, const Func &rhs) {
			return {lhs.calc(rhs.c1), lhs.calc(rhs.c2), rhs.lim};
		}
	} nd[MAXN << 2];

	void pushUp(int p) {
		nd[p] = nd[ls(p)] + nd[rs(p)];
	}

	void upd(int p, int l, int r, int x, Func v) {
		if (l == r) {
			nd[p] = v;
			return;
		}
		int mid = l + r >> 1;
		if (x <= mid) upd(ls(p), l, mid, x, v);
		else upd(rs(p), mid + 1, r, x, v);
		pushUp(p);
	}

	Func query(int p, int l, int r, int x, int y) {
		if (x <= l && y >= r) return nd[p];
		int mid = l + r >> 1;
		if (x <= mid && y > mid) return query(ls(p), l, mid, x, y) + query(rs(p), mid + 1, r, x, y);
		else if (x <= mid) return query(ls(p), l, mid, x, y);
		else return query(rs(p), mid + 1, r, x, y);
	}
#undef ls
#undef rs
} sgt;

void dfs1(int u) {
	sz[u] = 1;
	for (int v : T[u]) {
		dep[v] = dep[u] + 1;
		dfs1(v);
		sz[u] += sz[v];
		if (sz[v] > sz[hson[u]]) hson[u] = v;
	}
}

void proc(int u) {
	if (hson[u]) {
		G[u] = a[u] + tr.find(rt[u], a[u], 0);
		H[u] = a[u] + b[hson[u]] + tr.find(rt[u], a[u] + b[hson[u]], 0);
	} else {
		G[u] = H[u] = a[u];
	}
	sgt.upd(1, 1, n, dfn[u], {H[u], G[u], H[u]});
}

void dfs2(int u, int tp) {
	top[u] = tp;
	dfn[u] = ++stmp;
	rdfn[stmp] = u;

	if (!hson[u]) {
		bot[u] = u;
		proc(u);
		r[u] = a[u];
		return;
	}
	
	dfs2(hson[u], tp);
	bot[u] = bot[hson[u]];
	for (int v : T[u]) {
		if (v == hson[u]) continue;
		dfs2(v, v);
		tr.ins(rt[u], tr.create(b[v], {r[v], v}));
	}
	proc(u);
	r[u] = r[hson[u]] < H[u] ? H[u] : G[u];
}

void upd(int u) {
	while (top[u] != 1) {
		u = top[u];
		tr.del(rt[fa[u]], {r[u], u});
		r[u] = sgt.query(1, 1, n, dfn[u], dfn[bot[u]]).calc(r[hson[u]]);
		tr.ins(rt[fa[u]], tr.create(b[u], {r[u], u}));
		proc(u = fa[u]);
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin >> n >> q;
	for (int i = 2; i <= n; ++i) {
		cin >> fa[i];
		T[fa[i]].emplace_back(i);
	}
	for (int i = 1; i <= n; ++i) cin >> a[i];
	for (int i = 1; i <= n; ++i) cin >> b[i];

	tr.nd[0].mn = inf;
	dfs1(1);
	dfs2(1, 1);

	while (q--) {
		int op;
		cin >> op;

		if (op == 1) {
			int u, x, y;
			cin >> u >> x >> y;

			a[u] = x;
			proc(u);
			upd(u);

			b[u] = y;
			if (fa[u]) {
				if (hson[fa[u]] != u) {
					tr.del(rt[fa[u]], {r[u], u});
					tr.ins(rt[fa[u]], tr.create(b[u], {r[u], u}));
				}
				proc(fa[u]);
				upd(fa[u]);
			}
		} else {
			int x, y;
			cin >> x >> y;
			r[x] = sgt.query(1, 1, n, dfn[x], dfn[bot[x]]).calc(r[hson[x]]);
			r[y] = sgt.query(1, 1, n, dfn[y], dfn[bot[y]]).calc(r[hson[y]]);
			cout << (r[x] < r[y] || (r[x] == r[y] && x < y)) << '\n';
		}
	}
	return 0;
}
posted @ 2026-08-04 16:47  P2441M  阅读(1)  评论(0)    收藏  举报