POJ3237 Tree(树剖)

本文详细介绍了一种高效的树链剖分算法,该算法能够快速处理树形结构上的查询和更新操作,尤其适用于需要频繁进行路径操作的问题。文章通过一个具体的编程实现案例,解释了如何构建树链剖分的数据结构,并提供了详细的代码实现,包括如何处理边权值的更改、取反和查询等操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Tree
Time Limit: 5000MS Memory Limit: 131072K
Total Submissions: 11539 Accepted: 2977

Description

You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edges are numbered 1 through N − 1. Each edge is associated with a weight. Then you are to execute a series of instructions on the tree. The instructions can be one of the following forms:

CHANGE i vChange the weight of the ith edge to v
NEGATE a bNegate the weight of every edge on the path from a to b
QUERY a bFind the maximum weight of edges on the path from a to b

Input

The input contains multiple test cases. The first line of input contains an integer t (t ≤ 20), the number of test cases. Then follow the test cases.

Each test case is preceded by an empty line. The first nonempty line of its contains N (N ≤ 10,000). The next N − 1 lines each contains three integers ab and c, describing an edge connecting nodes a and b with weight c. The edges are numbered in the order they appear in the input. Below them are the instructions, each sticking to the specification above. A lines with the word “DONE” ends the test case.

Output

For each “QUERY” instruction, output the result on a separate line.

Sample Input

1

3
1 2 1
2 3 2
QUERY 1 2
CHANGE 1 3
QUERY 1 2
DONE

Sample Output

1
3

题意:给出n-1条双向边,每个边有权值

有三种操作:

1.改变第i条边的边权为w

2.对u到v路径上的边权值取反

3.查询u到v路径上的最大边的边权


开始做树链剖分的第一题,树链剖分代码太长了,有很多细节需要注意


首先是建树,由于这里查询的是边,于是需要用边建树,其实和用点建树没什么区别,只需要把边权放在深度较大的那个点上即可

操作1:直接线段树单点更新维护

操作2:对于求u到v路径上最大边权,由于有取反操作,那么在线段树维护最大值时需要同时维护最大值和最小值,在取反的时候最小值就变成了最大值,最大值变成了最小值,这样就可以O(1)维护最值

操作3:查询

另外还要注意,在更新和查询的时候,如果在同一条重链上,更新深度较低的那点的重儿子到深度较高的那点即可(因为边权都放在了深度较高的点上,如果此时还更新了最上边那个点就多更新了一条边)

还有很多小细节,比如最大值可能为负,所以ans的初值需要定义为 -inf,具体看代码把

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cassert>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
const int N=1e5+10;
const int M=(N<<2);
typedef long long ll;
ll siz[N],top[N],son[N],dep[N],faz[N],tid[N],rnk[N];
ll a[N],first[N],cnt,n,tot;
struct node
{
    ll l,r,minn,maxx,lazy;
    ll mid()
    {
        return (l+r)>>1;
    }
}t[N<<2];
struct edge
{
    ll v,w,next;
}e[N<<1];
void dfs1(ll u,ll father,ll depth)
{
    dep[u]=depth;
    faz[u]=father;
    siz[u]=1;
    for(ll i=first[u];~i;i=e[i].next)
    {
        ll v=e[i].v;
        if(v!=father)
        {
            a[v]=e[i].w;
            dfs1(v,u,depth+1);
            siz[u]+=siz[v];
            if(son[u]==-1||siz[v]>siz[son[u]])
                son[u]=v;
        }
    }
}
void dfs2(ll u,ll t)
{
    top[u]=t;
    tid[u]=++cnt;
    rnk[cnt]=u;
    if(son[u]==-1)return;
    dfs2(son[u],t);
    for(ll i=first[u];~i;i=e[i].next)
    {
        ll v=e[i].v;
        if(v!=son[u]&&v!=faz[u])
            dfs2(v,v);
    }
}
void pushup(ll rt)
{
    t[rt].maxx=max(t[rt<<1].maxx,t[rt<<1|1].maxx);
    t[rt].minn=min(t[rt<<1].minn,t[rt<<1|1].minn);
}
void nega(ll rt)
{
    t[rt].lazy^=1;
    swap(t[rt].maxx,t[rt].minn);
    t[rt].maxx*=-1;
    t[rt].minn*=-1;
}
void pushdown(ll rt)
{
    if(t[rt].lazy)
    {
        nega(rt<<1);
        nega(rt<<1|1);
        t[rt].lazy=0;
    }
}
void build(ll l,ll r,ll rt)
{
    t[rt].l=l,t[rt].r=r,t[rt].lazy=0;
    if(l==r)
    {
        t[rt].minn=t[rt].maxx=a[rnk[l]];
        return;
    }
    ll m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushup(rt);
}
void update(ll ql,ll qr,ll rt)
{
    if(ql<=t[rt].l&&t[rt].r<=qr)
    {
        nega(rt);
        return;
    }
    pushdown(rt);
    ll m=t[rt].mid();
    if(ql<=m)update(ql,qr,rt<<1);
    if(qr>m)update(ql,qr,rt<<1|1);
    pushup(rt);
}
void change(ll q,ll rt,ll w)
{
    if(t[rt].l==t[rt].r)
    {
        t[rt].maxx=t[rt].minn=w;
        return;
    }
    pushdown(rt);
    ll m=t[rt].mid();
    if(q<=m)
    change(q,rt<<1,w);
    else
        change(q,rt<<1|1,w);
    pushup(rt);
}
ll query(ll ql,ll qr,ll rt)
{
    if(ql<=t[rt].l&&t[rt].r<=qr)
    return t[rt].maxx;
    pushdown(rt);
    ll m=t[rt].mid();
    ll ans=-1e18;
    if(ql<=m)ans=max(ans,query(ql,qr,rt<<1));
    if(qr>m)ans=max(ans,query(ql,qr,rt<<1|1));
    return ans;
}
ll query_path(ll x,ll y)
{
    ll ans=-1e18;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])swap(x,y);
            ans=max(ans,query(tid[top[x]],tid[x],1));
            x=faz[top[x]];
    }
    if(x!=y)
    {
        if(tid[x]>tid[y])swap(x,y);
        ans=max(ans,query(tid[son[x]],tid[y],1));
    }

    return ans;
}
void update_path(ll x,ll y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])swap(x,y);
            update(tid[top[x]],tid[x],1);
            x=faz[top[x]];
    }
    if(x!=y)
    {
        if(tid[x]>tid[y])swap(x,y);
        update(tid[son[x]],tid[y],1);
    }
}
void init()
{
    mem(son,-1);
    mem(first,-1);
    tot=cnt=0;
}
void add(ll u,ll v,ll w)
{
    e[tot].v=v;
    e[tot].w=w;
    e[tot].next=first[u];
    first[u]=tot++;
}
int main()
{
    ll t,u,v,w,x,y;
    char op[10];
    scanf("%lld",&t);
    while(t--)
    {
        scanf("%lld",&n);
        init();
        for(ll i=2; i<=n; i++)
        {
            scanf("%lld%lld%lld",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);
        }
        dfs1(1,1,1);
        dfs2(1,1);
        build(1,n,1);
        while(scanf("%s",op)&&op[0]!='D')
        {
            scanf("%lld%lld",&x,&y);
            if(op[0]=='C')
            {
                ll now=x*2-1;
                u=tid[e[now].v]>tid[e[now^1].v]?tid[e[now].v]:tid[e[now^1].v];
                change(u,1,y);
            }
            else if(op[0]=='N')
            update_path(x,y);
            else
                printf("%lld\n",query_path(x,y));
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值