Problem
poj.org/problem?id=2763
vjudge.net/contest/28982#problem/B
Reference
Meaning
一棵 n 个点的树,每条边都有一个边权。一个人开始在 s 结点。两种操作:
0 u
:询问人从 s 走到 u 要走多久,之后人就走到了 u 这个点(即 s 更新为 u)1 i w
:把第 i 条边的权值改成 w
Analysis
考虑树链剖分。权值是在边上,要把权值“转移”到点上。
策略是:对于每一条边 e 的两个端点 f 和 t,如果 depth[f] > depth[t],就把边权记到 f 上,否则记到 t 上(即把边权转移到深度更大的端点处)。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100000;
int head[N+1], from[N<<1], to[N<<1], nxt[N<<1], w[N<<1];
void add_edge(int f, int t, int c, int sz)
{
from[sz] = f;
to[sz] = t;
w[sz] = c;
nxt[sz] = head[f];
head[f] = sz;
}
int dep[N+1], fa[N+1], son[N+1], tsz[N+1];
int tid[N+1], pos[N+1], top[N+1], tm;
void heavy_light(int v, int f, int d)
{
dep[v] = d;
fa[v] = f;
tsz[v] = 1;
son[v] = -1;
for(int i = head[v]; ~i; i = nxt[i])
if(to[i] != f)
{
heavy_light(to[i], v, d + 1);
tsz[v] += tsz[to[i]];
if(son[v] == -1 || tsz[to[i]] > tsz[son[v]])
son[v] = to[i];
}
}
void decompose(int v, int t)
{
top[v] = t;
pos[v] = ++tm;
tid[pos[v]] = v;
if(son[v] == -1)
return;
decompose(son[v], t);
for(int i = head[v]; ~i; i = nxt[i])
if(to[i] != fa[v] && to[i] != son[v])
decompose(to[i], to[i]);
}
int tree[N+1<<2];
inline void pushup(int x)
{
tree[x] = tree[x<<1] + tree[x<<1|1];
}
void update(int p, int v, int l, int r, int rt)
{
if(l == r)
{
tree[rt] = v;
return;
}
int m = l + r >> 1;
if(p > m)
update(p, v, m+1, r, rt<<1|1);
else
update(p, v, l, m, rt<<1);
pushup(rt);
}
int sum(int ql, int qr, int l, int r, int rt)
{
if(ql <= l && r <= qr)
return tree[rt];
if(qr < l || r < ql)
return 0;
int m = l + r >> 1;
return sum(ql, qr, l, m, rt<<1) +
sum(ql, qr, m+1, r, rt<<1|1);
}
int query(int l, int r, int n)
{
int ans = 0;
while(top[l] != top[r])
{
if(dep[top[l]] < dep[top[r]])
swap(l, r);
ans += sum(pos[top[l]], pos[l], 1, n, 1);
l = fa[top[l]];
}
if(l != r) // 必须要这个判断
{
if(dep[l] > dep[r])
swap(l, r);
// 当处于同一条链
// 上面的那端要往下退一个点 -> son[l]
// 因为 l 点记得权值是它上面那条边的
// 而那条边不在查询路径上
ans += sum(pos[son[l]], pos[r], 1, n, 1);
}
return ans;
}
int a[N+1] = {0}; // 结点 i 的初始权值
int mp[N]; // 第 i 条边的权值记载 mp[i] 结点上
void build(int l, int r, int rt)
{
if(l == r)
{
tree[rt] = a[tid[l]];
return;
}
int m = l + r >> 1;
build(l, m, rt<<1);
build(m+1, r, rt<<1|1);
pushup(rt);
}
int main()
{
int n, q, s;
scanf("%d%d%d", &n, &q, &s);
memset(head, ~0, sizeof head);
for(int i = 1, f, t, c, sz = 0; i < n; ++i)
{
scanf("%d%d%d", &f, &t, &c);
add_edge(f, t, c, sz++);
add_edge(t, f, c, sz++);
}
heavy_light(1, 0, 1);
tm = 0;
decompose(1, 1);
// 对每一条边,都映射到它深度更大的那个端点
// 并把它的权值记载那个映射点上
for(int i = 1, j = 0; i < n; ++i, j += 2)
if(dep[from[j]] > dep[to[j]])
a[mp[i] = from[j]] = w[j];
else
a[mp[i] = to[j]] = w[j];
build(1, n, 1);
for(int op, v, e; q--; )
{
scanf("%d", &op);
if(op == 0)
{
scanf("%d", &v);
printf("%d\n", query(s, v, n));
s = v;
}
else
{
scanf("%d%d", &e, &v);
// 更新边就变成更新点
// mp[e] 找到要更新的点
update(pos[mp[e]], v, 1, n, 1);
}
}
return 0;
}