SPOJ QTREE 树链剖分

375. Query on a tree

Problem code: QTREE

You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1.

We will ask you to perfrom some instructions of the following form:

  • CHANGE i ti : change the cost of the i-th edge to ti
    or
  • QUERY a b : ask for the maximum edge cost on the path from node a to node b

Input

The first line of input contains an integer t, the number of test cases (t <= 20). t test cases follow.

For each test case:

  • In the first line there is an integer N (N <= 10000),
  • In the next N-1 lines, the i-th line describes the i-th edge: a line with three integers a b c denotes an edge between ab of cost c (c <= 1000000),
  • The next lines contain instructions "CHANGE i ti" or "QUERY a b",
  • The end of each test case is signified by the string "DONE".

There is one blank line between successive tests.

Output

For each "QUERY" operation, write one integer representing its result.

Example

Input:
1

3
1 2 1
2 3 2
QUERY 1 2
CHANGE 1 3
QUERY 1 2
DONE

Output:
1
3

题解:  抄板子
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include<iostream>
using namespace std ;
#define mem(a) memset(a,0,sizeof(a))
#define pb push_back
#define fi first
#define se second
#define MP make_pair
typedef long long ll;

const int maxn = 100010;
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;

struct sss {
  int to,next;
}edge[maxn >> 1];
int head[maxn],tot;
int top[maxn];//top[v]表示v所在的重链的顶端节点
int fa[maxn]; //父亲节点
int deep[maxn];//深度
int num[maxn];//num[v]表示以v为根的子树的节点数
int p[maxn];//p[v]表示v与其父亲节点的连边在线段树中的位置
int fp[maxn];//和p数组相反
int son[maxn];//重儿子
int pos;
void init() {
   mem(head);tot=1;mem(son);
   pos=1;
}
void add(int u,int v) {
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void dfs1(int u,int pre,int d) {///第一遍dfs求出fa,deep,num,son
    deep[u]=d;
    fa[u]=pre;
    num[u]=1;
    for(int i=head[u];i;i=edge[i].next) {
        int v=edge[i].to;
        if(v != pre) {
            dfs1(v,u,d+1);
            num[u] += num[v];
            if(son[u] == 0 || num[v] > num[son[u]]) son[u]=v;
        }
    }
}
void dfs2(int u,int sp) {/////第二遍dfs求出top和p
    top[u]=sp;
    if(son[u] != 0) {
        p[u] = pos++;
        fp[p[u]] = u;
        dfs2(son[u],sp);
    }
    else {
        p[u] = pos++;
        fp[p[u]] = u;
        return ;
    }
    for(int i=head[u];i;i=edge[i].next) {
        int v=edge[i].to;
        if(v != son[u] && v != fa[u]) {
            dfs2(v,v);
        }
    }
}
////线段树
struct ss{
   int l,r;
   int M;
}tr[maxn >> 2];
void build(int k,int s,int t) {
    tr[k].l=s;tr[k].r=t;
    tr[k].M=0;
    if(s==t) return ;
    int mid = (s+t) >> 1;
    build(k<<1,s,mid);
    build(k<<1|1,mid+1,t);
}
void pushup(int k) {
    tr[k].M=max(tr[k<<1].M,tr[k<<1|1].M);
}
void update(int k,int x,int c) {
    if(tr[k].l==x&&tr[k].r==x) {
        tr[k].M=c;
        return ;
    }
    int mid = (tr[k].l+tr[k].r) >> 1;
    if(x<=mid) update(k<<1,x,c);
    else update(k<<1|1,x,c);
    pushup(k);
}
int ask(int k,int s,int t) {
    if(tr[k].l==s&&tr[k].r==t) {
        return tr[k].M;
    }
    int mid = (tr[k].l+tr[k].r) >> 1;
    if(t<=mid) return ask(k<<1,s,t);
    else if(s>mid) return ask(k<<1|1,s,t);
    else return max(ask(k<<1,s,mid),ask(k<<1|1,mid+1,t));
}
int finds(int u,int v) {
    int fx = top[u],fy = top[v];
    int tmp=0;
    while(fx!=fy) {
        if(deep[fx] < deep[fy]) {
            swap(fx,fy);
            swap(u,v);
        }
        tmp = max(tmp,ask(1,p[fx],p[u]));
        u = fa[fx];
        fx = top[u];
    }
    if(u==v) return tmp;
    if(deep[u] > deep[v]) swap(u,v);
    return max(tmp,ask(1,p[son[u]],p[v]));
}
int main() {

    int T,e[maxn][3],n;
    scanf("%d",&T);
    while(T--) {
        init();
        scanf("%d",&n);
        for(int i=1;i<n;i++) {
            scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]);
            add(e[i][0],e[i][1]);add(e[i][1],e[i][0]);
        }
        dfs1(1,0,0);//
        dfs2(1,1);//
        build(1,1,pos-1);
        for(int i=1;i<n;i++) {
            if(deep[e[i][0]] > deep[e[i][1]]) swap(e[i][0],e[i][1]);
            update(1,p[e[i][1]],e[i][2]);
        }
        char ch[10];
        int u,v;
        while(~scanf("%s",ch)) {
            if(ch[0]==‘D‘) break;
            scanf("%d%d",&u,&v);
            if(ch[0]==‘Q‘) printf("%d\n",finds(u,v));
            else update(1,p[e[u][1]],v);
        }
    }
    return 0;
}
时间: 2024-12-19 11:27:18

SPOJ QTREE 树链剖分的相关文章

【学术篇】SPOJ QTREE 树链剖分

发现链剖这东西好久不写想一遍写对是有难度的.. 果然是熟能生巧吧.. WC的dalao们都回来了 然后就用WC的毒瘤题荼毒了我们一波, 本来想打个T1 44分暴力 然后好像是特判写挂了还是怎么的就只能得28pts.. 重新见到这些失踪的dalao灰常开心, 于是想让自己心情稍微差一点, 就想着把自己昨天写WA的QTREE重构一遍吧.. 于是重构的sb链剖果然挂掉了... 出现了各种各样的漏洞... 忘记各种各样的句子, 然而退化成了暴力小数据也随便过看不出来啊~~~ 但是还是在1h之内调对了_(

SPOJ375.QTREE树链剖分

题意:一个树,a b c 代表a--b边的权值为c.CHANGE x y  把输入的第x条边的权值改为y,QUERY x y 查询x--y路径上边的权值的最大值. 第一次写树链剖分,其实树链剖分只能说是一种思想.树链剖分  就是 先选择从根节点到叶子节点的最长的路径的权值对应到线段树上,然后从一个子树的根节点到叶子的最长路径的权值对应到线段树上这样直到把所有的点都处理了,然后就是线段树区间查询最值了. 具体可以看这个博客.http://blog.sina.com.cn/s/blog_6974c8

SPOJ 375 树链剖分

点击打开链接 题意:给个树和树上的权值,两个操作,Q u v,问u到v的边上的最大权值,C u v,将第u条边的权值改为v 思路:今天学了学树链剖分,这题是个检验模版的题目,理论我是解释不清楚的,自己在九野聚聚那学来的一份模版 #include <vector> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <iostream> #include <a

SPOJ 375 QTREE系列-Query on a tree (树链剖分)

题目地址:SPOJ 375 树链剖分第一发! 果然是个貌似很高级的数据结构,其实就是把树的边从树形结构转化成了线性结构,从而可以用线段树或树状数组之类的数据结构进行快速维护.从而将时间缩到n*log(2*n). 这题用的线段树维护的. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #incl

SPOJ 375 QTREE - Query on a tree(树链剖分)

题目链接:http://www.spoj.com/problems/QTREE/en/ 题意:  一棵树 n 个节点,每条边上有权值,同时有两个操作: (1)更改操作:CHANGE i ti(把第 i 条边上的权值改为 ti). (2)查询操作:QUERY a b(查询 a 到 b 的路径上权值最大的边的权值). 思路(树链剖分): 看到这种区间查询的题想要用数据结构优化,提高时间效率一般会想到线段树.可是这次操作的对象并不是一组序列, 无法直接使用线段树.这时,我们可以做些转化:对树作树链剖分

SPOJ - QTREE 375 Query on a tree 树链剖分+线段树

操作1:修改第k条边权. 操作2:询问两点间最大边权. 树链剖分,然后线段树维护最大值 #include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> #include<set> #include<map> #include<queue> #include<vector> #inclu

SPOJ QTREE Query on a tree ——树链剖分 线段树

[题目分析] 垃圾vjudge又挂了. 树链剖分裸题. 垃圾spoj,交了好几次,基本没改动却过了. [代码](自带常数,是别人的2倍左右) #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 20005 int T,n,fr[maxn],h[maxn],to[maxn],ne[maxn]

spoj 375 QTREE - Query on a tree 树链剖分

题目链接 给一棵树, 每条边有权值, 两种操作, 一种是将一条边的权值改变, 一种是询问u到v路径上最大的边的权值. 树链剖分模板. #include <iostream> #include <vector> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <map> #include <set&g

SPOJ - QTREE(树链剖分+单点更新+区间最大值查询)

题意:给出n个点n-1条边的树,有两个操作,一个是查询节点l到r的边的最大值,然后指定边的更改权值. 题解:差不多是树链剖分的模版题,注意每个点表示的边是连向其父亲节点的边. #include <iostream> #include <cstring> #include <cstdio> using namespace std; const int M = 1e4 + 10; struct Edge { int v , next; }edge[M << 1]