POJ - 2763 Housewife Wind 【LCA + 树状数组 or 树链剖分】

传送门
题目大意:

知道了一颗有 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;
        }
    }
}

当然也可以用树链剖分做, 这样做的话这就是个裸题了….

猜你喜欢

转载自blog.csdn.net/Anxdada/article/details/81305022
今日推荐