spoj 375 (树链剖分)

spoj 375

题目:http://www.spoj.com/problems/QTREE/

题目大意:给你一棵有N个结点的树,执行两种操作:(1)CHANGE a b,把某一条树枝上的权值改为b。(2)QUERY a b,询问a到b的路径上最大的权值是多少。

思路:继上一道 Free tour 之后漆子超论文里的第三道例题,上两道是点,这次是用的是树链剖分,具体解题思想,可以看漆子超的论文。

如果单独纯模拟,虽然期望复杂度是O(logN),但是最坏情况下会达到O(N),大量询问下,肯定会TLE。引入树链剖分,他有三个性质(具体看论文),用线段数进行维护,复杂度可以降为O(logN*logN)。

转载的上一篇文章是介绍树链剖分的,写的很好,至少简单易懂。。 = = ,就是我感觉那篇文章的有一个地方写错了,就是w[ u ]指的是 u 的父边的权值,那么,根节点的 w 应该是不存在的,如果设边在线段树上的编号从1开始,那么w[ root ] 应该为0,而他写得程序里是1(目前我是这么认为的,毕竟现在自己也不是很懂)。第一次做树链剖分的题,第一遍TLE,发现是有个小地方写错了,改过来后,就AC了,好开心!

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

const int INF = 0x0fffffff ;

const int MAXN = 11111 ;

struct Edge
{
    int t,len,next;
} edge[MAXN<<1];

int head[MAXN],tot;

void add_edge(int s,int t,int len)
{
    edge[tot].t=t;
    edge[tot].len = len;
    edge[tot].next = head[s];
    head[s] = tot++;
}

struct Edge_id
{
    int a,b,len;
} edge_id[MAXN];

int node[MAXN<<2];

int father[MAXN],dep[MAXN],num[MAXN],son[MAXN],top[MAXN],w[MAXN];
int tot_w;

void dfs(int u,int fa,int level)
{
    dep[u] = level;
    father[u]=fa;
    num[u]=1;
    son[u]=-1;
    int maxx=0;
    for(int e = head[u];e!=-1;e=edge[e].next)
    {
        int v = edge[e].t;
        if(v!=fa)
        {
            dfs(v,u,level+1);
            num[u]+=num[v];
            if(num[v]>maxx)
            {
                maxx = num[v];
                son[u]=v;
            }
        }
    }
}

void build(int u,int tp)
{
    w[u] = ++tot_w;
    top[u]=tp;
    if(son[u]!=-1)
        build(son[u],tp);
    for(int e = head[u];e!=-1;e =edge[e].next)
    {
        int v = edge[e].t;
        if(v!=father[u]&&v!=son[u])
            build(v,v);
    }
}

void update(int l,int r,int rt,int pos,int c)
{
    if(l==r)
    {
        node[rt]=c;
        return ;
    }
    int m = l+r>>1;
    if(pos<=m) update(lson,pos,c);
    else update(rson,pos,c);
    node[rt] = max(node[rt<<1],node[rt<<1|1]);
}

int query(int l,int r,int rt,int a,int b)
{
    if(a<=l&&b>=r)
        return node[rt];
    int m = l+r>>1;
    int cnt1=-INF,cnt2=-INF;
    if(a<=m)
        cnt1 = query(lson,a,b);
    if(b>m)
        cnt2 = query(rson,a,b);
    return max(cnt1,cnt2);
}

int find(int a,int b)
{
    int fa = top[a];
    int fb = top[b];
    int tmp =0 ;
    while(fa!=fb)
    {
        if(dep[fa]>dep[fb])
        {
            swap(a,b);
            swap(fa,fb);
        }
        tmp = max(tmp,query(1,tot_w,1,w[fb],w[b]));
        b = father[fb];
        fb = top[b];
    }
    if(a==b) return tmp;
    if(dep[a]>dep[b])
    {
        swap(a,b);
    }
    return max(tmp,query(1,tot_w,1,w[son[a]],w[b]));
}

int n;

void init()
{
    int root =1;
    dfs(root,root,1);
    tot_w = -1;
    build(root,root);
    for(int i=1;i<n;i++)
    {
        if(dep[edge_id[i].a]>dep[edge_id[i].b])
            swap(edge_id[i].a,edge_id[i].b);
        update(1,tot_w,1,w[edge_id[i].b],edge_id[i].len);
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        int a,b,c;
        tot=0;
        memset(head,-1,sizeof(head));
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            add_edge(a,b,c);
            add_edge(b,a,c);
            edge_id[i].a=a;
            edge_id[i].b=b;
            edge_id[i].len=c;
        }
        init();
        char str[11];
        while(1)
        {
            scanf("%s",str);
            if(str[0]=='D')
                break;
            int a,b;
            scanf("%d%d",&a,&b);
            if(str[0]=='C')
            {
                int va = edge_id[a].a;
                int vb = edge_id[a].b;
                if(dep[va]>dep[vb])
                    swap(va,vb);
                update(1,tot_w,1,w[vb],b);
            }
            else printf("%d\n",find(a,b));
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值