[JSOI2016] 轻重路径 题解

前言

题目链接:LOJ洛谷Hydro & bzoj

题意简述

在二叉树上,不断删除叶子,你要维护其树链剖分后重儿子编号和。如果两个孩子大小相同,在一开始连向左儿子,或者保持修改前的连接。

\(n \leq 2 \times 10^5\)

题目分析

有分块的、有二分的,那我来讲一讲我的想法——树剖维护树剖。

首先反转操作,不断加叶子,那么可能发生变化的就是根到当前结点上,轻儿子变成重儿子。由于树链剖分的性质,这样的个数不会超过 \(\log\)。考虑根据它的树剖,不断跳到重链顶 \(u\),判断 \(\operatorname{fa}(u)\) 的重儿子会不会从 \(\operatorname{bro}(u)\) 变成 \(u\)

假设发生了变化,那么会发生什么?记 \(\operatorname{tail}(u)\) 表示一条以 \(u\)\(\operatorname{top}\) 的重链的链底。记 \(v = \operatorname{top}(\operatorname{fa}(u))\),那么我们要把 \(\operatorname{bro}(u) \sim \operatorname{tail}(v)\)\(\operatorname{top}\) 修改为 \(\operatorname{bro}(u)\),并把 \(u \sim \operatorname{tail}(u)\)\(\operatorname{top}\) 修改为 \(v\),而且 \(\operatorname{tail}(\operatorname{bro}(u))\) 修改为 \(\operatorname{tail}(v)\)\(\operatorname{tail}(v)\) 修改为 \(\operatorname{tail}(u)\)。当然,需要维护重儿子。

注意到,当 \(\operatorname{bro}(u)\) 不存在时,这时候只可能时 \(u\) 刚刚加入到二叉树里。特殊处理一下就可以了。

树链修改,考虑用树剖。嗯?显然不能用它这个变来变去的树剖,不然上不了数据结构。所以要把最终的二叉树先剖了,用一棵线段树支持树链修改。

至于如何判断会不会发生改变,我们显然困扰在 \(\operatorname{siz}(u) = \operatorname{siz}(\operatorname{bro}(u))\) 的情况,这种情况会保持当前操作之前的形态。如果 \(u\)\(\operatorname{bro}(u)\) 之前都没有任何操作,那么就是保持在左儿子。否则,考虑最后一次操作是在 \(u\) 还是 \(\operatorname{bro}(u)\),其所在的结点就是操作前的重儿子。为什么?由题意正向考虑,在这一次操作前,其所在节点的 \(\operatorname{siz}\) 大,是在删除后才相等,所以保留。

这个可以无脑主席树和 dfs 序。相当于查询子树里第 \(i\) 个版本的最大值。

注意到,维护 \(\operatorname{siz}\) 也可以用树链剖分无脑维护。

预处理出完成所有操作后的树的树链剖分,很水。但是把重儿子编号记成了轻儿子编号还能有 \(60\) 分?

总的时间复杂度是 \(\Theta(m \log^3 n)\)

代码

有一些细节要注意。无脑的坏处是码量略大。

#include <iostream>
#include <cstdio>
using namespace std;

int n, m;
int L[200010], R[200010];
int del[200010];
long long ans[200010];
bool mark[200010];
int when[200010];

int fa[200010], top[200010], dpt[200010];
int son[200010], siz[200010];
void dfs(int now) {
	siz[now] = 1;
	if (L[now]) dpt[L[now]] = dpt[now] + 1, fa[L[now]] = now, dfs(L[now]), siz[now] += siz[L[now]], siz[L[now]] > siz[son[now]] && (son[now] = L[now]);
	if (R[now]) dpt[R[now]] = dpt[now] + 1, fa[R[now]] = now, dfs(R[now]), siz[now] += siz[R[now]], siz[R[now]] > siz[son[now]] && (son[now] = R[now]);
}
int dfn[200010], timer;
int dfnL[200010], dfnR[200010];
void redfs(int now, int tp) {
	dfn[dfnL[now] = ++timer] = now;
	top[now] = tp;
	if (son[now]) redfs(son[now], tp);
	if (L[now] && L[now] != son[now]) redfs(L[now], L[now]);
	if (R[now] && R[now] != son[now]) redfs(R[now], R[now]);
	dfnR[now] = timer;
}

int TOP[200010], SON[200010], SIZ[200010];
int ED[200010];
void DFS(int now) {
	SIZ[now] = !mark[now];
	if (L[now]) DFS(L[now]), SIZ[now] += SIZ[L[now]], when[now] = max(when[now], when[L[now]]);
	if (R[now]) DFS(R[now]), SIZ[now] += SIZ[R[now]], when[now] = max(when[now], when[R[now]]);
	if (!mark[now]) {
		if (L[now] && !mark[L[now]] && siz[L[now]] > siz[R[now]]) {
			SON[now] = L[now];
		} else if (R[now] && !mark[R[now]] && siz[R[now]] > siz[L[now]]) {
			SON[now] = R[now];
		} else {
			if (L[now] && !mark[L[now]] && when[L[now]] > when[R[now]]) {
				SON[now] = L[now];
			} else if (R[now] && !mark[R[now]]){
				SON[now] = R[now];
			} else if (L[now] && !mark[L[now]]){
				SON[now] = L[now];
			} else {
				SON[now] = 0;
			}
		}
	}
}
void REDFS(int now, int tp) {
	TOP[now] = tp, ED[tp] = now;
	if (SON[now]) REDFS(SON[now], tp), ans[m + 1] += SON[now];
	if (L[now] && !mark[L[now]] && L[now] != SON[now]) REDFS(L[now], L[now]);
	if (R[now] && !mark[R[now]] && R[now] != SON[now]) REDFS(R[now], R[now]);
}

struct Segment_Tree {
	#define lson (idx << 1    )
	#define rson (idx << 1 | 1)
	
	struct node {
		int l, r;
		int top, siz;
		bool tag;
		int lazy;
	} tree[200010 << 2];
	
	void build(int idx, int l, int r) {
		tree[idx] = {l, r, 0, 0, false, 0};
		if (l == r) return tree[idx].top = TOP[dfn[l]], tree[idx].siz = SIZ[dfn[l]], void();
		int mid = (l + r) >> 1;
		build(lson, l, mid);
		build(rson, mid + 1, r);
	}
	
	void pushtag_top(int idx, int tp) {
		tree[idx].top = tp;
		tree[idx].tag = true;
	}
	
	void pushtag_lazy(int idx, int lazy) {
		tree[idx].lazy += lazy;
		tree[idx].siz += lazy;
	}
	
	void pushdown(int idx) {
		if (tree[idx].tag) {
			pushtag_top(lson, tree[idx].top);
			pushtag_top(rson, tree[idx].top);
			tree[idx].tag = false;
		}
		if (tree[idx].lazy) {
			pushtag_lazy(lson, tree[idx].lazy);
			pushtag_lazy(rson, tree[idx].lazy);
			tree[idx].lazy = 0;
		}
	}
	
	void modify_top(int idx, int l, int r, int val) {
		if (tree[idx].l > r || tree[idx].r < l) return;
		if (l <= tree[idx].l && tree[idx].r <= r) return pushtag_top(idx, val);
		pushdown(idx);
		modify_top(lson, l, r, val);
		modify_top(rson, l, r, val);
	}
	
	void modify_lazy(int idx, int l, int r, int val) {
		if (tree[idx].l > r || tree[idx].r < l) return;
		if (l <= tree[idx].l && tree[idx].r <= r) return pushtag_lazy(idx, val);
		pushdown(idx);
		modify_lazy(lson, l, r, val);
		modify_lazy(rson, l, r, val);
	}
	
	int query_siz(int idx, int p) {
		if (tree[idx].l > p || tree[idx].r < p) return 0;
		if (tree[idx].l == tree[idx].r) return tree[idx].siz;
		pushdown(idx);
		return query_siz(lson, p) | query_siz(rson, p);
	}
	
	int query_top(int idx, int p) {
		if (tree[idx].l > p || tree[idx].r < p) return 0;
		if (tree[idx].l == tree[idx].r) return tree[idx].top;
		pushdown(idx);
		return query_top(lson, p) | query_top(rson, p);
	}
	
	#undef lson
	#undef rson
} yzh;

int bro(int x) {
	if (x == L[fa[x]]) return mark[R[fa[x]]] ? 0 : R[fa[x]];
	return mark[L[fa[x]]] ? 0 : L[fa[x]];
}

int gettop(int x) {
	return yzh.query_top(1, dfnL[x]);
}

int getsiz(int x) {
	return yzh.query_siz(1, dfnL[x]);
}

void modify_lazy(int u, int v) {
	while (top[u] != top[v]) {
		if (dpt[top[u]] < dpt[top[v]]) swap(u, v);
		yzh.modify_lazy(1, dfnL[top[u]], dfnL[u], 1);
		u = fa[top[u]];
	}
	if (dfnL[u] > dfnL[v]) swap(u, v);
	yzh.modify_lazy(1, dfnL[u], dfnL[v], 1);
}

void modify_top(int u, int v, int w) {
	while (top[u] != top[v]) {
		if (dpt[top[u]] < dpt[top[v]]) swap(u, v);
		yzh.modify_top(1, dfnL[top[u]], dfnL[u], w);
		u = fa[top[u]];
	}
	if (dfnL[u] > dfnL[v]) swap(u, v);
	yzh.modify_top(1, dfnL[u], dfnL[v], w);
}

struct Yet_another_Segment_Tree {
	struct node {
		int lson, rson;
		int val;
	} tree[200010 * 80];
	int tot;
	
	int root[200010];
	
	int copyNode(int idx) {
		return tree[++tot] = tree[idx], tot;
	}
	
	void modify(int &idx, int trl, int trr, int p, int v) {
		if (trl > p || trr < p) return;
		idx = copyNode(idx);
		tree[idx].val = max(tree[idx].val, v);
		if (trl == trr) return;
		int mid = (trl + trr) >> 1;
		modify(tree[idx].lson, trl, mid, p, v);
		modify(tree[idx].rson, mid + 1, trr, p, v);
	}
	
	int query(int idx, int trl, int trr, int l, int r) {
		if (!idx || trl > r || trr < l) return 0;
		if (l <= trl && trr <= r) return tree[idx].val;
		int mid = (trl + trr) >> 1;
		return max(query(tree[idx].lson, trl, mid, l, r), query(tree[idx].rson, mid + 1, trr, l, r));
	}
} ttrr;

bool check(int i, int u, int v) {
	if (i == 1) return L[fa[u]] == u;
	int resu = ttrr.query(ttrr.root[i - 1], 1, n, dfnL[u], dfnR[u]),
		resv = ttrr.query(ttrr.root[i - 1], 1, n, dfnL[v], dfnR[v]);
	if (!resu && !resv) return L[fa[u]] == u;
	return resu > resv;
}

signed main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) scanf("%d%d", &L[i], &R[i]);
	scanf("%d", &m);
	for (int i = 1; i <= m; ++i) scanf("%d", &del[i]), mark[del[i]] = true, when[del[i]] = i;
	dfs(1), redfs(1, 1);
	DFS(1), REDFS(1, 1);
	for (int i = 1; i <= m; ++i) {
		ttrr.root[i] = ttrr.root[i - 1];
		ttrr.modify(ttrr.root[i], 1, n, dfnL[del[i]], i);
	}
	yzh.build(1, 1, n);
	for (int i = m; i >= 1; --i) {
		int u = del[i];
		long long res = 0;
		mark[u] = false;
		modify_lazy(u, 1);
		yzh.modify_top(1, dfnL[u], dfnL[u], u);
		ED[u] = u;
		if (u == 1) continue;
		if (!bro(u)) {
			SON[fa[u]] = u;
			res += u;
			int ftop = gettop(fa[u]);
			ED[ftop] = ED[u];
			yzh.modify_top(1, dfnL[u], dfnL[u], ftop);
			u = ftop;
		}
		while (u != 1) {
			int v = bro(u);
			int sizu = getsiz(u), sizv = getsiz(v);
			int ftop = gettop(fa[u]);
			if (v && SON[fa[u]] == v && (sizu > sizv || (sizu == sizv && check(i, u, v)))) {
				res += u - v;
				SON[fa[u]] = u;
				ED[v] = ED[ftop];
				ED[ftop] = ED[u];
				modify_top(u, ED[u], ftop);
				modify_top(v, ED[v], v);
			}
			u = ftop;
		}
		ans[i] = ans[i + 1] + res;
	}
	for (int i = 1; i <= m + 1; ++i) {
		printf("%lld\n", ans[i]);
	}
	return 0;
}
posted @ 2024-08-05 21:53  XuYueming  阅读(12)  评论(0编辑  收藏  举报