传送门
题目大意:
知道了一颗有 n 个节点的树和树上每条边的权值,对应两种操作:
0 x 输出 当前节点到 x节点的最短距离,并移动到 x 节点位置
1 x val 把第 x 条边的权值改为 val
思路:
树上两个节点a,b的距离可以转化为 dis[a] + dis[b] - 2*dis[lca(a,b)]
其中 dis[i] 表示 i 节点到根的距离,
由于每次修改一条边,树中在这条边下方的 dis[] 值全都会受到影响,这样每条边都对应这一段这条边的管辖区,
可以深搜保存遍历该点的时间戳,p1[i] 表示第一次遍历到该点的时间戳, p2[i] 表示回溯到该点时的时间戳,这样每次
修改边 i 的时候就可以对区间 [ p1[i], p2[i] ] 进行成段更新,成段更新的方式可以在 位置 p1[i] 上加一个权值,在位置
p2[i]+1 上减去这个权值,求和时,sum( p1[i] ) 即为该点到根的距离
AC Code
const int maxn = 1e5+5;
int c[maxn], p1[maxn], p2[maxn];
int cnt, head[maxn];
int up[maxn][21], id[maxn];
int deep[maxn], dis[maxn];
int n, q, st, ti;
void add(int i, int x) {
while(i <= n) {
c[i] += x;
i += i&(-i);
}
}
int getsum(int i) {
int ans = 0;
while(i) {
ans += c[i];
i -= i&(-i);
}
return ans;
}
struct node {
int to, next, w, idx;
}e[maxn<<1];
void init() {
ti = 0; cnt = 0; Fill(head, -1); Fill(c, 0);
Fill(up,0); Fill(deep,0); Fill(dis, 0);
}
void add(int u, int v, int w, int idx) {
e[cnt] = {v, head[u], w, idx};
head[u] = cnt++;
}
void dfs_id(int u, int fa) {
p1[u] = ++ti;
for(int i = head[u] ; ~i ; i = e[i].next){
int to = e[i].to;
if(to == fa) continue;
dfs_id(to,u);
}
p2[u] = ti;
}
void dfs(int u,int fa,int d) {
deep[u] = d + 1;
for(int i = 1 ; i < 20 ; i ++) {
up[u][i] = up[up[u][i-1]][i-1];
}
for(int i = head[u] ; ~i ; i = e[i].next) {
int to = e[i].to;
if(to == fa) continue;
dis[to] = dis[u] + e[i].w;
id[e[i].idx] = e[i].to;
up[to][0] = u;
dfs(to, u, d+1);
}
}
int LCA_BZ(int u,int v) {
int mx = 0;
if(deep[u] < deep[v]) swap(u,v);
int k = deep[u] - deep[v];
for(int i = 0 ; i < 20 ; i ++) {
if((1<<i) & k) {
u = up[u][i];
}
}
if(u != v) {
for(int i = 19 ; i >= 0 ; i --) {
if(up[u][i] != up[v][i]){
u = up[u][i];
v = up[v][i];
}
}
u = up[u][0];
}
return u;
}
int a[maxn];
void solve() {
scanf("%d%d%d", &n ,&q, &st);
init();
for (int i = 1 ; i < n ; i ++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
a[i] = w;
add(u, v, w, i); add(v, u, w, i);
}
dfs_id(1, -1); dfs(1, -1, 0);
for (int i = 1 ; i < n ; i ++) {
add(p1[id[i]], a[i]); add(p2[id[i]]+1, -a[i]);
}
while(q--) {
int op, t, g;
scanf("%d", &op);
if (op) {
scanf("%d%d", &t, &g);
int tmp = a[t]; a[t] = g;
add(p1[id[t]], g-tmp); add(p2[id[t]]+1, tmp-g);
}
else {
int t; scanf("%d", &t);
printf("%d\n", getsum(p1[st]) + getsum(p1[t]) - 2*getsum(p1[LCA_BZ(st, t)]));
st = t;
}
}
}
当然也可以用树链剖分做, 这样做的话这就是个裸题了….