SPOJ QTREE Query on a tree 边权LCT

题目:

https://vjudge.net/problem/SPOJ-QTREE

题意:

给定一棵有n个点的树,有以下两种操作:

  1. CHANGE i ti 把第i条边的权值改为 ti
  2. QUERY a b: 查询 ab 之间的最大边权值

思路:

边权有两种解决方案,一种把边看做一个新点,从边的两个端点像这个新点连边,然后就跟点权一样了,另一种是把边权给深度的较深的儿子,但是这样不能换根,也就是不能进行树的分离和合并。所以一般用第一种

#include <bits/stdc++.h>

using namespace std;

const int N = 20000 + 10, INF = 0x3f3f3f3f;

struct edge
{
    int to, next;
}g[N*5];

int cnt, head[N];
int son[N][2], fat[N], key[N], rev[N], maxval[N];
int top, stk[N];
int val[N];

void add_edge(int v, int u)
{
    g[cnt].to = u, g[cnt].next = head[v], head[v] = cnt++;
}
void dfs(int v, int fa)
{
    fat[v] = fa;
    key[v] = maxval[v] = val[v];
    for(int i = head[v]; ~i; i = g[i].next)
    {
        int u = g[i].to;
        if(u == fa) continue;
        dfs(u, v);
    }
}
bool is_root(int x)
{
    return son[fat[x]][0] != x && son[fat[x]][1] != x;
}
void push_up(int x)
{
    maxval[x] = max(key[x], max(maxval[son[x][0]], maxval[son[x][1]]));
}
void push_down(int x)
{
    if(rev[x])
    {
        swap(son[x][0], son[x][1]);
        rev[son[x][0]] ^= 1, rev[son[x][1]] ^= 1;
        rev[x] ^= 1;
    }
}
void Rotate(int x)
{
    int y = fat[x], p = son[y][0] == x;
    son[y][!p] = son[x][p], fat[son[x][p]] = y;
    if(! is_root(y)) son[fat[y]][son[fat[y]][1]==y] = x;
    fat[x] = fat[y];
    son[x][p] = y, fat[y] = x;
    push_up(y);
}
void splay(int x)
{
    top = 0;
    stk[++top] = x;
    for(int i = x; !is_root(i); i = fat[i]) stk[++top] = fat[i];
    for(int i = top; i >= 1; i--) push_down(stk[i]);
    while(! is_root(x))
    {
        int y = fat[x], z = fat[y];
        if(is_root(y)) Rotate(x);
        else
        {
            if((x == son[y][0]) ^ (y == son[z][0])) Rotate(x), Rotate(x);
            else Rotate(y), Rotate(x);
        }
    }
    push_up(x);
}
void access(int x)
{
    int y = 0;
    while(x)
    {
        splay(x);
        son[x][1] = y;
        push_up(x);
        y = x, x = fat[x];
    }
}
void make_root(int x)
{
    access(x); splay(x);
    rev[x] ^= 1;
}
int find_root(int x)
{
    push_down(x);
    while(son[x][0]) x = son[x][0], push_down(x);
    return x;
}
void update(int x, int v)
{
    splay(x);
    key[x] = v;
    push_up(x);
    //splay(x);
}
int query(int x, int y)
{
    make_root(x);
    access(y); splay(y);
    return maxval[y];
}
//注释掉的查询:先查询出在原树中的lca,然后查询这两点到lca的两条链上的最值
//调用后x是原来x和y的lca,y和son[x][1]分别存着lca的2个儿子, 即原来x和y所在的2颗子树的根
//void Lca(int &x, int &y)
//{
//    access(y); y = 0;
//    while(x)
//    {
//        splay(x);
//        if(! fat[x]) break;
//        son[x][1] = y;
//        push_up(x);
//        y = x, x = fat[x];
//    }
//}
//int query(int x, int y)
//{
//    Lca(x, y);
//    //cout << x << " " << y << " " << son[x][1] << endl;
//    return max(key[x], max(maxval[y], maxval[son[x][1]]));
//}
void init()
{
    cnt = 0;
    memset(head, -1, sizeof head);
    memset(fat, 0, sizeof fat);
    memset(son, 0, sizeof son);
    memset(rev, 0, sizeof rev);
    memset(val, 0, sizeof val);
    memset(key, 0, sizeof key);
}
int main()
{
    int t, n;
    scanf("%d", &t);
    while(t--)
    {
        init();
        scanf("%d", &n);
        int a, b, c;
        for(int i = 1; i <= n-1; i++)
        {
            scanf("%d%d%d", &a, &b, &c);
            add_edge(a, n + i); add_edge(n + i, a);
            add_edge(b, n + i); add_edge(n + i, b);
            val[n+i] = c;
        }
        dfs(1, 0);
        char opt[15];
        while(scanf("%s", opt), opt[0] != 'D')
        {
            scanf("%d%d", &a, &b);
            if(opt[0] == 'C') update(a+n, b);
            else printf("%d\n", query(a, b));
        }
    }
    return 0;
}

第二种方法:

#include <bits/stdc++.h>

using namespace std;

const int N = 10000 + 10;

struct edge
{
    int to, cost, next;
    int idx;
}g[N*2];

int cnt, head[N];

int son[N][2], fat[N], key[N], maxval[N];
int id[N];
void init()
{
    cnt = 0;
    memset(head, -1, sizeof head);
    memset(son, 0, sizeof son);
    memset(fat, 0, sizeof fat);
    memset(key, 0, sizeof key);
    memset(maxval, 0, sizeof maxval);
}
void add_edge(int v, int u, int cost, int idx)
{
    g[cnt].to = u, g[cnt].cost = cost, g[cnt].idx = idx, g[cnt].next = head[v], head[v] = cnt++;
}
void dfs(int v, int fa)
{
    fat[v] = fa;
    for(int i = head[v]; ~i; i = g[i].next)
    {
        int u = g[i].to;
        if(u == fa) continue;
        id[g[i].idx] = u;
        key[u] = g[i].cost;
        dfs(u, v);
    }
}
void push_up(int x)
{
    maxval[x] = max(key[x], max(maxval[son[x][0]], maxval[son[x][1]]));
}
bool is_root(int x)
{
    return son[fat[x]][0] != x && son[fat[x]][1] != x;
}
void Rotate(int x)
{
    int y = fat[x], p = son[y][0] == x;
    son[y][!p] = son[x][p], fat[son[x][p]] = y;
    if(! is_root(y)) son[fat[y]][son[fat[y]][1]==y] = x;
    fat[x] = fat[y];
    son[x][p] = y, fat[y] = x;
    push_up(y);
}
void splay(int x)
{
    while(! is_root(x))
    {
        int y = fat[x], z = fat[y];
        if(is_root(y)) Rotate(x);
        else
        {
            if((x == son[y][0]) ^ (y == son[z][0])) Rotate(x), Rotate(x);
            else Rotate(y), Rotate(x);
        }
    }
    push_up(x);
}
void access(int x)
{
    int y = 0;
    while(x)
    {
        splay(x);
        son[x][1] = y;
        push_up(x);
        y = x, x = fat[x];
    }
}
void lca(int &x, int &y)
{
    access(y);
    y = 0;
    while(x)
    {
        splay(x);
        if(! fat[x]) return;
        son[x][1] = y;
        push_up(x);
        y = x, x = fat[x];
    }
}
void update(int x, int v)
{
    access(x);
    key[x] = v;
    push_up(x);
}
int query(int x, int y)
{
    lca(x, y);
    return max(maxval[y], maxval[son[x][1]]);
    //这里不能取key[x]的值,因为x虽然是两点的lca,但权值却不属于两点到lca的路径上
}
int main()
{
    int t, n;
    char opt[20];
    scanf("%d", &t);
    while(t--)
    {
        init();
        scanf("%d", &n);
        int a, b, c;
        for(int i = 1; i <= n-1; i++)
        {
            scanf("%d%d%d", &a, &b, &c);
            add_edge(a, b, c, i); add_edge(b, a, c, i);
        }
        dfs(1, 0);
        while(scanf("%s", opt), opt[0] != 'D')
        {
            scanf("%d%d", &a, &b);
            if(opt[0] == 'C') update(id[a], b);
            else printf("%d\n", query(a, b));
        }
    }
    return 0;
}
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值