Spoj 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 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<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
#define tu nod[u]
#define tv nod[v]
#define e tree[id]
#define lson tree[id*2]
#define rson tree[id*2+1]
const int maxn=10005;
int N,pid;
vector<int> G[maxn];
struct node
{
    int top,fa,deep;//top所属树链的顶点,fa父节点,deep深度
    int s,p,fp,son; //s以它为子树的大小,p新编号,fp相对p反向的,这题里面没什么用,son重链所指向的点
}nod[maxn];
struct edge
{
    int u,v,c;
    edge(int u=0,int v=0,int c=0):u(u),v(v),c(c){}
}E[maxn];
void init()
{
    pid=0;
    for(int i=0;i<maxn;i++) G[i].clear(),nod[i].son=-1; //初始化
}
void dfs(int u,int fa,int deep)
{
    tu.fa=fa,tu.deep=deep,tu.s=1;  //保存父节点,深度,大小为1
    int Size=G[u].size();
    for(int i=0;i<Size;i++)
    {
        int v=G[u][i];
        if(v==fa) continue;  //父亲不管
        dfs(v,u,deep+1); 
        tu.s+=tv.s;  //加上大小
        if(tu.son==-1||tv.s>nod[tu.son].s) tu.son=v; //找重链所指向的点
    }
}
void Div(int u,int top)
{
    tu.top=top;  //重链顶点
    tu.p=++pid;  //重新编号
    nod[tu.p].fp=u;  
    if(tu.son!=-1) Div(tu.son,top); //有重链继续往下找
    else return;
    int Size=G[u].size();
    for(int i=0;i<Size;i++)
    {
        int v=G[u][i];
        if(v==tu.fa||v==tu.son) continue;  
        Div(v,v); //新的重链
    }
}
struct Tree   //线段树维护区间最大值
{
    int le,ri,v;
}tree[4*maxn];
void pushup(int id){ e.v=max(lson.v,rson.v); }
void Build_tree(int id,int le,int ri)
{
    e.le=le,e.ri=ri,e.v=0;
    if(le==ri) return;
    int mid=(le+ri)/2;
    Build_tree(id*2,le,mid);
    Build_tree(id*2+1,mid+1,ri);
}
void Update(int id,int k,int v)
{
    int le=e.le,ri=e.ri;
    if(le==ri){ e.v=v; return; }
    int mid=(le+ri)/2;
    if(k<=mid) Update(id*2,k,v);
    else Update(id*2+1,k,v);
    pushup(id);
}
int Query(int id,int x,int y)
{
    int le=e.le,ri=e.ri;
    if(x<=le&&ri<=y) return e.v;
    int mid=(le+ri)/2;
    int ret=0;
    if(x<=mid) ret=max(ret,Query(id*2,x,y));
    if(y>mid)  ret=max(ret,Query(id*2+1,x,y));
    return ret;
}
int Find(int u,int v)
{
    int f1=tu.top,f2=tv.top;  
    int ret=0;
    while(f1!=f2) //不在同一条链上
    {
        if(nod[f1].deep<nod[f2].deep)  //总是让u是深度大的点
        {
            swap(f1,f2);
            swap(u,v);
        }
        ret=max(ret,Query(1,nod[f1].p,tu.p));  //查询这条链
        u=nod[f1].fa; f1=tu.top;   //跳到父节点去
    }
    if(u==v) return ret;  //相同
    if(tu.deep>tv.deep) swap(u,v); 
    ret=max(ret,Query(1,nod[tu.son].p,tv.p)); //查询同一条链上的这段区间
    return ret;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&N);
        init();
        for(int i=1;i<N;i++)
        {
            int u,v,c;
            scanf("%d%d%d",&u,&v,&c);
            E[i]=edge(u,v,c);   //
            G[u].push_back(v);   
            G[v].push_back(u);
        }
        dfs(1,0,0); 
        Div(1,1);
        Build_tree(1,1,pid); //建树
        for(int i=1;i<N;i++)
        {
            edge& t=E[i];
            int& u=t.u;
            int& v=t.v;
            if(tu.deep>tv.deep) swap(u,v);
            Update(1,tv.p,t.c);   //修改边权
        }
        char op[15];
        while(scanf("%s",op)!=EOF)
        {
            int u,v;
            if(op[0]=='D') break;
            scanf("%d%d",&u,&v);
            if(op[0]=='Q') printf("%d\n",Find(u,v));  //查询
            else Update(1,nod[E[u].v].p,v); //更新
        }
    }
    return 0;
}
View Code

 

posted @ 2016-07-31 08:57  wust_ouyangli  阅读(211)  评论(0编辑  收藏  举报