动态树解决树上操作

一 问题描述

一棵树的节点编号为 1~N ,边的编号为 1~N-1,每条边都带有权值。在树上执行一系列指令,形式如下。

二 输入和输出

1 输入

输入包含多个测试用例。第 1 行为测试用例的数量T(T≤20)。每个测试用例的前面都有一个空行。第 1 个非空行包含 N(N≤10,000)。接下来的 N -1 行,每行都包含 3 个整数 a 、b  和 c ,表示边的两个节点 a 和 b 及该边的权值 c 。边按输入的顺序编号。若在行中出现单词“DONE”,则标志着结束。

2 输出

对每条 QUERY 指令,都单行输出结果。

三 输入和输出样例

1 输入样例

1

3

1 2 1

2 3 2

QUERY 1 2

CHANGE 1 3

QUERY 1 2

DONE

2 输出样例

1

3

四 分析 

本问题可以将边权看作点权,对一条边,让深度 dep 较大的节点存储权值,例如边 u -v ,边权为 w ,若dep[u] > dep[v],则视 u 的权值为 w ;或者在 u-v 之间增加一个虚节点,该虚节点的点权为 w,所有实节点的点权都为 -inf。

五 算法设计

1 点更新

将第 i 条边的权值更新为 v 。第 i 条边是第 n+i 个节点,更新该节点的值。

2 区间更新

将点 a 到 b 路径上每条边的权值都变为其相反数。首先切分 a -b 路径,然后对树根 b 更新并打懒标记。

3 区间最值查询

查询点 a 到 b 路径上边的最大权值。首先切分 a - b 路径,然后输出树根 b 的最大权值。

六 代码

 

package com.platform.modules.alg.alglib.poj3237;

public class Poj3237A {
    private int inf = 0x3f3f3f3f;
    private int N = 40005;
    int n, top;
    int c[][] = new int[N][2];
    int fa[] = new int[N];
    int v[] = new int[N];
    int mx[] = new int[N];
    int mn[] = new int[N];
    int st[] = new int[N];
    int rev[] = new int[N];
    int reg[] = new int[N];
    public String output = "";

    public String cal(String input) {
        int x, y, w;
        String opt;
        String[] line = input.split("\n");
        n = Integer.parseInt(line[0]);
        for (int i = 0; i < mx.length; i++) {
            mx[i] = -inf;
            mn[i] = inf;
        }
        for (int i = 1; i < n; i++) {
            String[] info = line[i].split(" ");
            x = Integer.parseInt(info[0]);
            y = Integer.parseInt(info[1]);
            w = Integer.parseInt(info[2]);
            v[i] = -inf;
            v[n + i] = w;
            link(x, n + i);
            link(n + i, y);
        }
        v[n] = -inf;
        int count = 0;
        while (true) {
            String[] query = line[n + count++].split(" ");
            opt = query[0];
            if (opt.charAt(0) == 'D') break;
            x = Integer.parseInt(query[1]);
            y = Integer.parseInt(query[2]);


            if (opt.charAt(0) == 'C') change(n + x, y);
            else if (opt.charAt(0) == 'Q') query(x, y);
            else if (opt.charAt(0) == 'N') neg(x, y);
        }


        return output;
    }

    void update(int x) {
        int l = c[x][0], r = c[x][1];
        mx[x] = Math.max(mx[l], mx[r]);
        mn[x] = Math.min(mn[l], mn[r]);
        if (v[x] != -inf && v[x] != inf) {
            mx[x] = Math.max(mx[x], v[x]);
            mn[x] = Math.min(mn[x], v[x]);
        }
    }

    // 先修改 + 打懒标
    void phr(int x) {
        reg[x] ^= 1;
        v[x] = -v[x];
        mx[x] = -mx[x];
        mn[x] = -mn[x];
        int temp = mx[x];
        mx[x] = mn[x];
        mn[x] = temp;
    }

    void pushdown(int x) {
        int l = c[x][0], r = c[x][1];
        if (rev[x] != 0) {
            rev[l] ^= 1;
            rev[r] ^= 1;
            rev[x] ^= 1;
            int temp = c[x][0];
            c[x][0] = c[x][1];
            c[x][1] = temp;
        }
        if (reg[x] != 0) {
            if (l > 0) phr(l);
            if (r > 0) phr(r);
            reg[x] ^= 1;
        }
    }

    boolean isroot(int x) {
        return c[fa[x]][0] != x && c[fa[x]][1] != x;
    }

    void rotate(int x) {
        int y = fa[x], z = fa[y], l, r;
        if (c[y][0] == x) l = 0;
        else l = 1;
        r = l ^ 1;
        if (!isroot(y)) {
            if (c[z][0] == y) c[z][0] = x;
            else c[z][1] = x;
        }
        fa[x] = z;
        fa[y] = x;
        fa[c[x][r]] = y;
        c[y][l] = c[x][r];
        c[x][r] = y;
        update(y);
        update(x);
    }

    void splay(int x) {
        top = 0;
        st[++top] = x;
        for (int i = x; !isroot(i); i = fa[i])
            st[++top] = fa[i];
        while (top > 0) pushdown(st[top--]);
        while (!isroot(x)) {
            int y = fa[x], z = fa[y];
            if (!isroot(y)) {
                if (c[y][0] == x ^ c[z][0] == y) {
                    rotate(x);
                } else {
                    rotate(y);
                }
            }
            rotate(x);
        }
    }

    void access(int x) {
        for (int t = 0; x > 0; t = x, x = fa[x]) {
            splay(x);
            c[x][1] = t;
            update(x);
        }
    }

    void makeroot(int x) {
        access(x);
        splay(x);
        rev[x] ^= 1;
    }

    void link(int x, int y) {
        makeroot(x);
        fa[x] = y;
    }

    void split(int x, int y) {
        makeroot(x);
        access(y);
        splay(y);
    }

    void cut(int x, int y) {
        split(x, y);
        c[y][0] = fa[c[y][0]] = 0;
        update(y);
    }

    int findroot(int x) {
        access(x);
        splay(x);
        while (c[x][0] > 0) x = c[x][0];
        return x;
    }

    void change(int x, int w) {
        splay(x);
        v[x] = w;
    }

    void query(int x, int y) {
        split(x, y);
        output += mx[y] + "\n";
    }

    void neg(int x, int y) {
        split(x, y);
        phr(y);
    }
}

七 测试

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值