1. 程式人生 > >SPOJ375——Query on a tree(樹鏈剖分模板詳解以及入門)

SPOJ375——Query on a tree(樹鏈剖分模板詳解以及入門)

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 a
    b 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 <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
using namespace std;

const int MAXN = 20010;
struct Edge
{
    int to,next;
}edge[MAXN*2];
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;

int n;

void init()
{
    tot = 0;
    memset(head,-1,sizeof(head));
    pos = 1;//序號其實是從1開始?
    memset(son,-1,sizeof(son));
}
void addedge(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 != -1; i = edge[i].next)
    {
        int v = edge[i].to;
        //因為路徑是雙向的,所以不能等於父及誒單
        if(v != pre)
        {
            //先遞迴地找到兒子節點的深度,父節點,子節點數目等資訊
            dfs1(v,u,d+1);
            //更新u節點的兒子數目
            num[u] += num[v];
            if(son[u] == -1 || num[v] > num[son[u]])//求出重兒子
                son[u] = v;
        }
    }
}

//因為對於輕兒子來說,top[u]=u,對於重兒子來說,如果son[v]!=-1,那麼top[v]=top[son[v]]
void getpos(int u,int sp) //第二遍dfs求出top和p
{
    top[u] = sp;
    //先找重兒子
    if(son[u] != -1)
    {
        //把邊的位置標記一下
        p[u] = pos++;
        //fp相當於是p的反函式?
        fp[p[u]] = u;
        //更新重兒子
        getpos(son[u],sp);
    }
    //如果到了葉子節點
    else
    {
        //不再向下dfs
        p[u] = pos++;
        fp[p[u]] = u;
        return;
    }
    //更新其他的節點
    for(int i = head[u] ; i != -1; i = edge[i].next)
    {
        int v = edge[i].to;
        if(v != son[u] && v != fa[u])
            getpos(v,v);
    }
}

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int MAX[MAXN<<2];
int val[MAXN<<2];

void pushup(int rt){
    MAX[rt]=max(MAX[rt<<1],MAX[rt<<1|1]);
}

void build(int l,int r,int rt){
    if(l==r){
        MAX[rt]=val[l];
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    pushup(rt);
}

void update(int p,int x,int l,int r,int rt){
    if(l==r){
        MAX[rt]=x;
        return;
    }
    int m=(l+r)>>1;
    if(p<=m)
        update(p,x,lson);
    else
        update(p,x,rson);
    pushup(rt);
}

int query(int L,int R,int l,int r,int rt){
    if(l>=L&&r<=R){
        return MAX[rt];
    }
    int m=(l+r)>>1;
    int res=0;
    if(m>=L)
        res=max(res,query(L,R,lson));
    if(R>m)
        res=max(res,query(L,R,rson));
    return res;
}

int _find(int u,int v){
    int f1=top[u],f2=top[v];//先找到兩個端點的重鏈頂端節點,如果是輕兒子,就是它本身
    int temp=0;
    while(f1!=f2){
        //從深度較深的開始查詢
        if(deep[f1]<deep[f2]){
            swap(f1,f2);
            swap(u,v);
        }
        //查詢一條重鏈上的最大值
        temp=max(temp,query(p[f1],p[u],1,n,1));
        u=fa[f1];f1=top[u];
    }
    //如果f1=f2代表在同一條重鏈上m,如果u=v代表更新結束
    if(u==v)
        return temp;
    if(deep[u]>deep[v])
        swap(u,v);
    return max(temp,query(p[son[u]],p[v],1,n,1));
}

int e[MAXN][3];
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        init();
        scanf("%d",&n);
        getchar();
        for(int i=0;i<n-1;i++){
            scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]);
            addedge(e[i][0],e[i][1]);
            addedge(e[i][1],e[i][0]);
        }
        dfs1(1,0,0);
        getpos(1,1);
        for(int i=0;i<n-1;i++){
            if(deep[e[i][0]]<deep[e[i][1]])
                swap(e[i][0],e[i][1]);
            val[p[e[i][0]]]=e[i][2];
        }
        build(1,n,1);
        char op[10];
        int u,v;
        while(scanf("%s",op)){
            if(op[0]=='D') break;
            scanf("%d%d",&u,&v);
            if(op[0]=='Q')
                printf("%d\n",_find(u,v));
            else
                update(p[e[u-1][0]],v,1,n,1);
        }
    }
    return 0;
}