题目链接: I love tree
大致题意
给定一棵有 n n n个节点的树, 初始树中的各个节点权值为 0 0 0. 有 m m m次如下操作:
1 a b
给
(
a
,
b
)
(a, b)
(a,b)路径加上一个平方数列
1
,
4
,
9
,
.
.
.
1, 4, 9, ...
1,4,9,...
2 x
询问编号为
x
x
x的点的权值.
解题思路
树链剖分
我们首先考虑对于一个序列加平方数列的操作.
我们发现就是 ➡️这个题⬅️ (题目的弱化版, 如果没做过推荐先做一下)
我们考虑对于树链上的操作: 比较容易想到可以通过树剖把树上问题转化为区间问题.
由于树链上的编号是一段一段的, 并不连续, 因此我们难以通过维护高阶差分的方式来给区间加上等差数列, 于是考虑通过维护二次函数的方式.
不同于序列上, 我们需要额外维护出, 当前这条链上的节点, 对应到序列中应该是第几个位置.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10;
vector<int> edge[N]; //树上各个点之间的边
int p[N], dep[N], sz[N], son[N];
// 父节点 深度 节点大小 重儿子
void dfs1(int x = 1, int fa = 0) { // x = 树根节点
p[x] = fa, dep[x] = dep[fa] + 1, sz[x] = 1; // son[x] = 0;
for (auto& to : edge[x]) {
if (to == fa) continue;
dfs1(to, x);
sz[x] += sz[to]; // 特别的, 如果边权->点权, 应记录w[to] = 边权.
if (sz[to] > sz[son[x]]) son[x] = to; //更新重儿子
}
}
int id[N], top[N], ind;
// 新编号 新值 重链顶 当前用到的编号
void dfs2(int x = 1, int tp = 1) { // x = 树根节点, tp = 树根节点
id[x] = ++ind, top[x] = tp;
if (!son[x]) return; //叶子结点
dfs2(son[x], tp); //先遍历重儿子
for (auto& to : edge[x]) {
if (to == p[x] or to == son[x]) continue;
dfs2(to, to);
}
}
struct node {
int l, r;
ll val;
ll lazy1, lazy2, lazy3; // lazy1*(x^2) -2*id*lazy2 lazy3
}t[N << 2];
void pushdown(node& op, ll lazy1, ll lazy2, ll lazy3) {
op.val += 1ll * op.l * op.l * lazy1 + -2ll * op.l * lazy2 + lazy3;
op.lazy1 += lazy1, op.lazy2 += lazy2, op.lazy3 += lazy3;
}
void pushdown(int x) {
if (!t[x].lazy1 and !t[x].lazy2 and !t[x].lazy3) return;
pushdown(t[x << 1], t[x].lazy1, t[x].lazy2, t[x].lazy3);
pushdown(t[x << 1 | 1], t[x].lazy1, t[x].lazy2, t[x].lazy3);
t[x].lazy1 = t[x].lazy2 = t[x].lazy3 = 0;
}
void build(int l, int r, int x = 1) {
t[x] = { l, r, 0, 0, 0, 0 };
if (l == r) return;
int mid = l + r >> 1;
build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
}
void modify(int l, int r, int c, int x = 1) {
if (l <= t[x].l and r >= t[x].r) {
pushdown(t[x], 1, c, 1ll * c * c);
return;
}
pushdown(x);
int mid = t[x].l + t[x].r >> 1;
if (l <= mid) modify(l, r, c, x << 1);
if (r > mid) modify(l, r, c, x << 1 | 1);
}
ll ask(int a, int x = 1) {
if (t[x].l == t[x].r) return t[x].val;
pushdown(x);
int mid = t[x].l + t[x].r >> 1;
return ask(a, x << 1 | (a > mid));
}
int lca(int a, int b) {
while (top[a] != top[b]) {
if (dep[top[a]] < dep[top[b]]) swap(a, b);
a = p[top[a]];
}
return id[a] < id[b] ? a : b;
}
void modify_route(int a, int b) {
int LCA = lca(a, b);
int l = 1, r = dep[a] + dep[b] - 2 * dep[LCA] + 1;
while (top[a] != top[b]) {
if (dep[top[a]] > dep[top[b]]) {
int qaq = id[top[a]] - l;
modify(id[top[a]], id[a], qaq);
l += id[a] - id[top[a]] + 1;
a = p[top[a]];
}
else {
int qaq = id[b] - id[top[b]] + 1;
qaq = r - qaq;
modify(id[top[b]], id[b], qaq);
r -= id[b] - id[top[b]] + 1;
b = p[top[b]];
}
}
if (id[a] < id[b]) {
int qaq = id[a] - l;
modify(id[a], id[b], qaq);
}
else {
int qaq = id[b] + r;
modify(id[b], id[a], qaq);
}
}
int main()
{
int n; cin >> n;
rep(i, n - 1) {
int a, b; scanf("%d %d", &a, &b);
edge[a].push_back(b), edge[b].push_back(a);
}
dfs1(), dfs2();
build(1, n);
int m; cin >> m;
rep(i, m) {
int tp; scanf("%d", &tp);
if (tp == 1) {
int a, b; scanf("%d %d", &a, &b);
modify_route(a, b);
}
else {
int x; scanf("%d", &x);
printf("%lld\n", ask(id[x]));
}
}
return 0;
}