[Ynoi2009] rla1rmdq 题解

题目描述

给定一棵 \(n(1\le n\le 2\times 10^5)\) 个节点的树,树有边权,与一个长为 \(n\) 的序列 \(a(1\le a_i\le n)\)

定义节点 \(x\) 的父亲为 \(fa(x)\),根 \(rt\) 满足 \(fa(rt)=rt\)

定义节点 \(x\) 的深度 \(dep(x)\) 为其到根简单路径上所有边权和。

\(m(1\le m\le 2\times 10^5)\) 次操作:

1 l r:对于 \(l \le i \le r\)\(a_i := fa(a_i)\)

2 l r:查询对于 \(l \le i \le r\),最小的 \(dep(a_i)\)​。

空间限制:64MB。

思路

可以对序列进行分块。

在同一个块中,会存在一些点永远无法造成贡献,而此时我们可以考虑不去跳这些点。准确来说,对于两个点 \(x\)\(y\),若 \(y\) 当前所在的点已经被 \(x\) 所经过,那么 \(x\) 对答案的贡献一定比 \(y\) 对答案的贡献要优,所以此时我们没有必要继续跳 \(y\) 这个点,于是可以将它删除。

可以发现,每个块最多会跳过 \(n\) 个点,所以所有块在这种情况下跳的次数仅为 \(O(n\sqrt{n})\)

考虑对散块的处理,此时我们需要对这个散块进行重构:具体的,将这整个块总共跳的次数记录下来,然后将块内所有点跳上去(这里可以用重链剖分,是 \(O(n\log n)\) 的),再把散点跳到父亲,然后重新统计答案。

因为空间限制只有 64MB,所以可以用 bitset 来存储每个块内的信息。

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
namespace FastIO {}
using namespace FastIO;
const int MAXN = 2e5 + 5, SIZ = 200, MAXM = 1005;
const ll inf = 0x3f3f3f3f3f3f3f3f; 
int n, N, m, rt;
int a[MAXN], dep[MAXN], top[MAXN], son[MAXN], siz[MAXN], fa[MAXN], dfn[MAXN], to[MAXN], tcnt;
int L[MAXM], R[MAXM], tag[MAXM], pos[MAXN], stk[MAXN], cnt;
ll dis[MAXN], sum[MAXN];
bitset<MAXN> flg[MAXM];
vector<int> S[MAXN];
vector<pair<int, int> > E[MAXN];
void dfs1(int x) {
	dep[x] = dep[fa[x]] + 1, siz[x] = 1;
	for(auto y : E[x]) {
		if(y.first == fa[x]) continue;
		fa[y.first] = x, dis[y.first] = dis[x] + y.second;
		dfs1(y.first);
		siz[y.first] = siz[x] + 1;
		if(siz[y.first] > siz[son[x]]) son[x] = y.first;
	}
}
void dfs2(int x, int tp) {
	top[x] = tp, dfn[x] = ++tcnt, to[tcnt] = x;
	if(son[x]) dfs2(son[x], tp);
	for(auto y : E[x]) {
		if(y.first == fa[x] || y.first == son[x]) continue;
		dfs2(y.first, y.first);
	}
}
int jump(int p, int k) {
	if(p == rt) return p;
	while(k) {
		int t = min(k, dfn[p] - dfn[top[p]]);
		p = to[dfn[p] - t], k -= t;
		if(!k) break;
		p = fa[p], k--;
	}
	return p;
}
void redo(int p) {
	if(!tag[p]) return;
	for(int i = L[p]; i <= R[p]; i++) a[i] = jump(a[i], tag[p]);
	tag[p] = 0;
}
void rebuild(int p) {
	sum[p] = inf;
	S[p].resize(R[p] - L[p] + 1);
	for(int i = L[p]; i <= R[p]; i++) S[p][i - L[p]] = a[i], sum[p] = min(sum[p], dis[a[i]]);
}
void update(int l, int r) {
	int pl = pos[l], pr = pos[r];
	if(pl == pr) {
		redo(pl);
		for(int i = l; i <= r; i++) a[i] = fa[a[i]];
		rebuild(pl);
	} else {
		redo(pl);
		for(int i = l; i <= R[pl]; i++) a[i] = fa[a[i]];
		rebuild(pl);
		for(int i = pl + 1; i <= pr - 1; i++) {
			tag[i]++;
			if(sum[i]) {
				cnt = 0;
				for(auto &j : S[i]) {
					j = fa[j];
					sum[i] = min(sum[i], dis[j]);
					if(flg[i][j]) continue;
					flg[i][j] = true;
					stk[cnt++] = j;
				}
				S[i].resize(cnt);
				for(int j = 0; j < cnt; j++) S[i][j] = stk[j];
			}
		}
		redo(pr);
		for(int i = L[pr]; i <= r; i++) a[i] = fa[a[i]];
		rebuild(pr);
	}
}
ll ask(int l, int r) {
	ll ans = inf;
	int pl = pos[l], pr = pos[r];
	if(pl == pr) {
		redo(pl), rebuild(pl);
		for(int i = l; i <= r; i++) ans = min(ans, dis[a[i]]);
	} else {
		redo(pl), rebuild(pl);
		for(int i = l; i <= R[pl]; i++) ans = min(ans, dis[a[i]]);
		for(int i = pl + 1; i <= pr - 1; i++) ans = min(ans, sum[i]);
		redo(pr), rebuild(pr);
		for(int i = L[pr]; i <= r; i++) ans = min(ans, dis[a[i]]);
	}
	return ans;
}
int main() {
	read(n, m, rt); N = (n + SIZ - 1) / SIZ;
	for(int i = 1, u, v, len; i < n; i++) {
		read(u, v, len);
		E[u].push_back(make_pair(v, len)), E[v].push_back(make_pair(u, len));
	}
	for(int i = 1; i <= n; i++) read(a[i]);
	dfs1(rt), dfs2(rt, rt), fa[rt] = rt;
	for(int i = 1; i <= N; i++) {
		L[i] = (i - 1) * SIZ + 1, R[i] = (i == N ? n : i * SIZ);
		for(int j = L[i]; j <= R[i]; j++) pos[j] = i;
		rebuild(i);
	}
	while(m--) {
		int opt, l, r;
		read(opt, l, r);
		if(opt == 1) update(l, r);	
		else write(ask(l, r), '\n');
	}
	fwrite(obuf, iP - obuf, 1, stdout);
	return 0;
} 
posted @ 2024-09-24 23:23  ddxrS  阅读(22)  评论(0)    收藏  举报