spoj375 重学树链剖分

题意:给定一棵树,告诉了每条边的权值,然后给出两种操作:

(1)把第i条边的权值改为val

#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
//siz[v]表示以v为根的子树的节点数
//dep[v]表示v的深度(根深度为1)
//top[v]表示v所在的链的顶端节点
//fa[v]表示v的父亲
//son[v]表示与v在同一重链上的v的儿子节点(姑且称为重儿子)
//tid[v]表示v与其父亲节点的连边(姑且称为v的父边)在线段树中的位置
const int maxn=50010;
int n;
int siz[maxn],dep[maxn],top[maxn],fa[maxn],son[maxn];
int tid[maxn];
int d[maxn][3];
int tim;
int num[maxn];
struct node
{
    int v;
    int next;
    int w;
}e[maxn*2];
int pre[maxn],cnt;
void init()
{
    cnt=0;
    tim=0;
    memset(pre,-1,sizeof(pre));
    memset(son,-1,sizeof(son));
}
void add(int u,int v,int w)
{
    e[cnt].v=v;
    e[cnt].next=pre[u];
    pre[u]=cnt++;
}
//树链剖分部分
void dfs1(int u,int father,int d)//求出dep,fa,siz,son
{
    dep[u]=d;
    fa[u]=father;
    siz[u]=1;
    for(int i=pre[u];i!=-1;i=e[i].next)
    {
        int v=e[i].v;
        if(v==father) continue;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(son[u]==-1||siz[v]>siz[son[u]])
            son[u]=v;
    }
}
void dfs2(int u,int tp)
{
     top[u]=tp;
     if(u!=1)
        tid[u]=++tim;
     if(son[u]==-1) return ;
     dfs2(son[u],tp);
     for(int i=pre[u];i!=-1;i=e[i].next)
     {
        int v=e[i].v;
        if(v!=son[u]&&v!=fa[u])
            dfs2(v,v);
     }
}
//线段树部分
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int inf=1e9;
int MAX[4*maxn];
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]=num[l];
        return ;
    }
   int mid=(l+r)>>1;
   build(lson);
   build(rson);
   pushup(rt);
}
void update(int l,int r,int rt,int p,int val)
{
    if(l==r)
    {
        MAX[rt]=val;
        return ;
    }
    int mid=(l+r)>>1;
    if(p<=mid)
        update(lson,p,val);
    else
        update(rson,p,val);
    pushup(rt);
}
void change(int x,int val)
{
    if(dep[d[x][0]]>dep[d[x][1]])
        update(1,n,1,tid[d[x][0]],val);
    else
        update(1,n,1,tid[d[x][1]],val);
}
int Query(int l,int r,int rt,int L,int R)
{
    if(L<=l&&R>=r)
        return MAX[rt];
    int mid=(l+r)>>1;
    int ret=-inf;
    if(L<=mid) ret=max(ret,Query(lson,L,R));
    if(R>mid)  ret=max(ret,Query(rson,L,R));
    return ret;
}
int query(int x,int y)
{
       int ans=-inf;
       while(top[x]!=top[y])
       {
           if(dep[top[x]]<dep[top[y]]) swap(x,y);//先搜top深度大的链
           ans=max(ans,Query(1,n,1,tid[top[x]],tid[x]));//轻边到重边也是连续的
           x=fa[top[x]];
       }
       if(dep[x]>dep[y]) swap(x,y);
       if(x!=y) ans=max(ans,Query(1,n,1,tid[x]+1,tid[y]));//注意是tid[x]+1;因为不能算<span style="font-family:Courier New;">X节点上面的边</span>
       return ans;

}
int main()
{
    char op[15];
    int t;
    cin>>t;
    while(t--)
    {
        init();
        scanf("%d",&n);
        for(int i=1;i<n;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            d[i][0]=a,d[i][1]=b,d[i][2]=c;
            add(a,b,c);
            add(b,a,c);
        }
        dfs1(1,-1,1);
        dfs2(1,1);
        //用边的孩子节点来表示该边
        for(int i=1;i<n;i++)
        {
            if(dep[d[i][0]]>dep[d[i][1]])
                num[tid[d[i][0]]]=d[i][2];
            else
                 num[tid[d[i][1]]]=d[i][2];
        }
        n=n-1;
        build(1,n,1);
        while(1)
        {
            scanf("%s",op);
            if(op[0]=='D') break;
            int a,b;
            scanf("%d%d",&a,&b);
            if(op[0]=='Q')
                printf("%d\n",query(a,b));
            else
                change(a,b);
        }

    }


}

(2)询问a,b路径上权值最大的边

 

分析:本题与HDU3966差不多,区别就是:HDU3966是告诉树中点权的值,这里是边权。

所以我们可以转化,用边的孩子节点来表示该边。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值