SPOJ QTREE Query on a tree 边权LCT

来源:互联网 发布:wifi共享精灵for mac 编辑:程序博客网 时间:2024/05/23 11:06

题目:

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;}
原创粉丝点击