比赛链接:link
题目
C 树剖
题目大意是说对于一棵树,初始所有点的权值
F
(
x
)
F(x)
F(x) 为0,有三种操作:①对于结点
x
x
x, 给定一个值
w
w
w,然后对于树上所有结点
y
y
y, 权值加上
w
−
d
i
s
t
(
x
,
y
)
w - dist(x, y)
w−dist(x,y)(包括本身);② 对于结点
x
,
F
(
x
)
=
m
i
n
(
x
,
0
)
x, F(x) = min(x, 0)
x,F(x)=min(x,0);③询问
F
(
x
)
F(x)
F(x)。
对于操作①的转化很巧妙:
w
−
d
i
s
t
(
x
,
y
)
=
w
−
d
e
p
(
x
)
−
d
e
p
(
y
)
+
2
d
e
p
(
l
c
a
(
x
,
y
)
)
w - dist(x, y) = w - dep(x) - dep(y) + 2dep(lca(x,y))
w−dist(x,y)=w−dep(x)−dep(y)+2dep(lca(x,y)),其中每次①操作的
w
−
d
e
p
(
x
)
w - dep(x)
w−dep(x) 都可以记录下来,
d
e
p
(
y
)
dep(y)
dep(y) 也是一个定值,而对于
d
e
p
(
l
c
a
(
x
,
y
)
)
dep(lca(x, y))
dep(lca(x,y)), 可以用树剖来记录,若记根节点
r
t
rt
rt 深度为 1,只需要让结点
x
x
x 到根结点路径上的结点加上 2,
d
e
p
(
l
c
a
(
x
,
y
)
)
dep(lca(x, y))
dep(lca(x,y))就是结点
y
y
y 到根结点的权值和。
然后对于操作②,若是某个结点的权值大于 0, 我们可以用数组去记录减去的数是多少。
刚学的树剖模板,千万不要忘记初始化
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const int maxn = 5e4 + 10;
const int INF = 0x3f3f3f3f;
const ll mod = 998244353;
int n, m;
struct edge //链式前向星
{
int to, next;
}e[maxn*2];
int head[maxn], num; //head为0表示搜索到了尽头
void add_edge(int u, int v)
{
e[++num].to = v;
e[num].next = head[u];
head[u] = num;
}
ll tree[maxn<<2], lazy[maxn<<2], dfn[maxn]; //线段树模板
void build(int node, int l, int r)
{
if(l == r)
{
tree[node] = 0;
lazy[node] = 0;
return;
}
int mid = (l + r) / 2;
build(node * 2, l, mid);
build(node * 2 + 1, mid + 1, r);
tree[node] = 0;
lazy[node] = 0;
}
void push_down(int node, int length)
{
if(lazy[node])
{
tree[2 * node] += lazy[node] * ((length + 1) / 2); //向子节进行更新
tree[2 * node + 1] += lazy[node] * (length / 2);
lazy[2 * node] += lazy[node]; //标记子节点
lazy[2 * node + 1] += lazy[node];
lazy[node] = 0; //父节点标记清空
}
}
ll query(int node, int l, int r, int x, int y)
{
if(x <= l && y >= r)
return tree[node];
push_down(node, r - l + 1); //多的地方
int mid = (l + r) / 2;
ll ans = 0LL;
if(x <= mid) ans += query(node * 2, l, mid, x, y);
if(y > mid) ans += query(node * 2 + 1, mid + 1, r, x, y);
return ans;
}
void update(int node, int l, int r, int x, int y, int c)
{
if(x <= l && y >= r)
{
tree[node] += (r - l + 1) * c;
lazy[node] += c;
return;
}
push_down(node, r - l + 1);
int mid = (l + r) / 2;
if(x <= mid) update(2*node, l, mid, x, y, c);
if(y > mid) update(2 * node + 1, mid + 1, r, x, y, c);
tree[node] = tree[node * 2] + tree[node * 2 + 1];
}
int siz[maxn], son[maxn], top[maxn], dep[maxn], faz[maxn], id[maxn], tol;
void dfs1(int x)
{
siz[x] = 1;
son[x] = 0;
for(int i = head[x]; i; i = e[i].next)
{
int v = e[i].to;
if(v == faz[x]) continue;
dep[v] = dep[x] + 1; //标记深度
faz[v] = x; //标记父亲
dfs1(v);
siz[x] += siz[v]; //记录子树个数
if(siz[v] > siz[son[x]]) son[x] = v; //标记重儿子
}
}
void dfs2(int x, int rt)
{
id[x] = ++tol; //记录在dfs序中的编号
dfn[tol] = x;
top[x] = rt; //记录所在链深度最小的结点
if(son[x]) dfs2(son[x], rt);
for(int i = head[x]; i; i = e[i].next)
{
int v = e[i].to;
if(v == faz[x] || v == son[x]) continue;
dfs2(v, v);
}
}
void updRange(int u, int v, int val)
{
while(top[u] != top[v]) //当两个点不在一条链上
{
if(dep[top[u]] < dep[top[v]]) swap(u, v); //让u为所在链顶端深度更大的那个点
update(1, 1, n, id[top[u]], id[u], val);
u = faz[top[u]];
}
if(dep[u] > dep[v]) swap(u, v); //让u为深度浅的那个点
update(1, 1, n, id[u], id[v], val);
}
ll qRange(int u, int v)
{
ll ans = 0;
while(top[u] != top[v]) //当两个点不在一条链上
{
if(dep[top[u]] < dep[top[v]]) swap(u, v); //让u为所在链顶端深度更大的那个点
ans += query(1, 1, n, id[top[u]], id[u]);
u = faz[top[u]];
}
if(dep[u] > dep[v]) swap(u, v); //让u为深度浅的那个点
ans += query(1, 1, n, id[u], id[v]);
return ans;
}
ll pre[maxn];
int main()
{
int t, mark, x, w;
scanf("%d", &t);
while(t--)
{
scanf("%d %d", &n, &m);
fill(head + 1, head + 1 + n, 0);
fill(pre + 1, pre + 1 + n, 0);
num = 0; tol = 0;
for(int i = 1; i < n; i++)
{
int x, y;
scanf("%d %d", &x, &y);
add_edge(x, y); add_edge(y, x);
}
dep[1] = 1;
dfs1(1);
dfs2(1, 1);
build(1, 1, n);
int cnt = 0;
ll cur = 0, tmp;
for(int i = 1; i <= m; i++)
{
scanf("%d", &mark);
if(mark == 1)
{
scanf("%d %d", &x, &w);
cur += w - dep[x];
cnt++;
updRange(1, x, 2);
}
else if(mark == 2)
{
scanf("%d", &x);
tmp = cur - cnt * dep[x] + qRange(1, x) + pre[x];
if(tmp > 0)
pre[x] -= tmp;
}
else
{
scanf("%d", &x);
tmp = cur - cnt * dep[x] + qRange(1, x) + pre[x];
printf("%lld\n", tmp);
}
}
}
}