P13663 「TPOI-5B」回忆

  • \(f_u\) 其实就是 \(u\) 子树的深度。
  • 每次加入一个点 \(u\)\(f\) 会加 \(1\) 的那些点形如 \(i\) 往上的一条链 \(u\to anc_u\)——其中 \(anc\) 需要我们求出。
  • 其它点的 \(f\) 不变。

考虑离线把树建出来,并尝试计算 \(anc\)

对于点 \(u\) 来说,其祖先 \(v\in \text{path}(i\to anc_u)\) 当且仅当 \(u\)\(v\) 子树里同深度最小点。

故而考虑维护 \(pos_{u,i}\) 表示 \(u\) 子树里,距离 \(u\)\(i\) 的最小编号点。长链剖分维护即可。

点击查看代码
#include <bits/stdc++.h>
#define FL(i, a, b) for (int i = (a); i <= (b); ++i)
#define FR(i, a, b) for (int i = (a); i >= (b); --i)
using namespace std;
typedef long long ll;
const int N = 5e6 + 10, MOD = 1e9 + 7;
int n, q, a[N], fa[N], mxD[N], son[N];
int buc[N], *bid = buc, *pos[N];
ll ans[N], val[N];
vector<int> G[N];
void Dfs1(int u) {
	val[u] = val[fa[u]] + a[u];
	for (int v: G[u]) {
		Dfs1(v);
		if (mxD[v] > mxD[son[u]])
			son[u] = v;
	}
	mxD[u] = mxD[son[u]] + 1;
}
int* NewArr(int len) {
	int* ret = bid;
	return bid += len, ret;
}
void Dfs2(int u) {
	pos[u][0] = u;
	if (son[u]) {
		pos[son[u]] = pos[u] + 1;
		Dfs2(son[u]);
	}
	for (int v: G[u]) {
		if (v != son[u]) {
			pos[v] = NewArr(mxD[v]);
			Dfs2(v);
			FL(i, 0, mxD[v] - 1) {
				int &x = pos[u][i + 1];
				int &y = pos[v][i];
				if (x > y) swap(x, y);
				ans[y] += val[fa[y]] - val[u];
			}
		}
	}
}
int main() {
	scanf("%d %d", &q, &a[1]), n = q + 1;
	FL(i, 2, q + 1) {
		scanf("%d %d", &fa[i], &a[i]);
		G[fa[i]].emplace_back(i);
	}
	Dfs1(1);
	pos[1] = NewArr(mxD[1]);
	Dfs2(1);
	FL(i, 0, mxD[1] - 1) {
		int x = pos[1][i];
		ans[x] += val[fa[x]];
	}
	FL(i, 2, q + 1) {
		ans[i] += ans[i - 1];
		printf("%lld\n", ans[i] %= MOD);
	}
	return 0;
}
posted @ 2025-08-09 14:45  徐子洋  阅读(17)  评论(0)    收藏  举报