基于边权的树链剖分+线段树维护单点修改、区间查询最大值 SPOJ - QTREE

题目链接

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

题意

给定一颗N结点的树,树有N-1条边,依此编号为1,2,3,…,N-1.对树执行下列操作:
QUERY a b:查询a结点到b结点的最大边权值
CHANGE i ti:将第i条边的权值修改为ti

分析

基于边权的树链剖分。
要维护树上路径相关问题,树链剖分可以高效解决。树链剖分是将树的结点映射到一个区间上,通过维护区间从而间接地维护树。
基于边权的关键在于两点:
(1)在dfs树确定了结点的深度后,将每一条边唯一对应一个结点。具体是对应结点深度更深的结点。
(2)查询路径a–>b时,由于LCA(a,b)所对应的边是其父结点与其所连边的权值,所以点LCA(a,b)不要查询。具体到代码中是到其重儿子结点截止。

AC代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+100;
vector<int> g[maxn];

//线段树单点修改,区间查询最大值
int s[maxn<<2];
int cnt,N;
void update(int p,int l,int r,int k,int v)//将·第k个元素值改为v
{
    if(l==r)
    {
        s[p]=v;
        return ;
    }
    int mid=(l+r)>>1;
    if(k<=mid) update(p*2,l,mid,k,v);
    else update(p*2+1,mid+1,r,k,v);
    s[p]=max(s[p*2],s[p*2+1]);
}
int query(int p,int l,int r,int x,int y)//查询区间[x,y]的最大值
{
    if(x<=l && r<=y) return s[p];
    int mid=(l+r)>>1;
    int ans=0;
    if(x<=mid) ans=max(ans,query(p*2,l,mid,x,y));
    if(y>mid) ans=max(ans,query(p*2+1,mid+1,r,x,y));
    return ans;
}


//树链剖分
int son[maxn],dep[maxn],fa[maxn],sz[maxn];
void dfs1(int u,int f,int d)
{
    dep[u]=d;
    fa[u]=f;
    sz[u]=1;
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(v==f) continue;
        dfs1(v,u,d+1);
        sz[u]+=sz[v];
        if(son[u]==-1 || sz[v]>sz[son[u]])
            son[u]=v;
    }
}

int top[maxn],id[maxn],pos[maxn];
void dfs2(int u,int topf)
{
    top[u]=topf;
    id[u]=++cnt;
    pos[cnt]=u;
    if(son[u]==-1) return ;
    dfs2(son[u],topf);
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(v==fa[u] || v==son[u]) continue;
        dfs2(v,v);
    }
}
int qPath(int x,int y)
{
    int ans=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        int L=id[top[x]],R=id[x];
        ans=max(ans,query(1,1,N,L,R));
        x=fa[top[x]];
    }
    if(x==y) return ans;
    if(dep[x]>dep[y]) swap(x,y);
    int L=id[son[x]],R=id[y];
    return ans=max(ans,query(1,1,N,L,R));
}
void init(int N)
{
    cnt=0;
    memset(son,-1,sizeof(son));
    memset(s,0,sizeof(s));
    for(int i=0;i<=N;i++) g[i].clear();
}
int p[maxn];
struct edge
{
    int u,v,w;
}e[maxn];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&N);
        init(N);
        for(int i=1;i<=N-1;i++)
        {
            int u,v,val;
            scanf("%d%d%d",&u,&v,&val);
            g[u].push_back(v);
            g[v].push_back(u);
            e[i].u=u;
            e[i].v=v;
            e[i].w=val;
        }
        dfs1(1,0,1);
        dfs2(1,1);
        for(int i=1;i<=N-1;i++)
        {
            int u=e[i].u,v=e[i].v,w=e[i].w;
            if(dep[u]<dep[v]) swap(u,v);
            update(1,1,N,id[u],w);
            p[i]=u;
        }

        char str[12];
        while(scanf("%s",str))
        {
            if(str[0]=='D') break;
            int a,b;
            scanf("%d%d",&a,&b);
            if(str[0]=='C') update(1,1,N,id[p[a]],b);
            else printf("%d\n",qPath(a,b));
        }
    }
    return 0;
}
/*
2
7
3 1 3
1 4 4
4 5 2
4 6 4
5 7 7
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值