BZOJ3531 旅行 树链剖分

本文介绍了一种在树形数据结构中利用线段树进行节点权重更新及查询的技术。通过对每种颜色单独使用线段树并动态分配内存的方式,实现了节点染色和权重修改等功能,并支持链上求和及最大值查询。文章提供了详细的代码实现。

题目大意:给定一棵树,点染色,修改点权,链上询问点权和或者最大值
数据规模10^5
主要思想是对每种颜色开一颗线段树,动态分配内存,染色时删除原来的点,加入到新线段树中即可。
需要注意的是不仅插入要更新信息,删除也要更新信息。
自己的代码:

#include<cstdio>
#include<cstring>
#define safe(x,a) (x?x->a:0)
#define gm 100001
using namespace std;
const int size=1<<16;
int p,v,ll,rr;
int n,q;
inline int __max(const int &a,const int &b)
{
    return a<b?b:a;
}
struct node
{
    node *l,*r;
    int max,sum;
    inline void* operator new(size_t);
    inline void operator delete(void*);
    void change(int x,int y)
    {
        if(x==y)
        {
            max=sum=v;
            return;
        }
        int mid=x+y>>1;
        if(p<=mid)
        {
            if(!l) l=new node;
            l->change(x,mid);
        }
        else
        {
            if(!r) r=new node;
            r->change(mid+1,y);
        }
        max=__max(safe(l,max),safe(r,max));
        sum=safe(l,sum)+safe(r,sum);
    }
    static void free(node*& kore,int x,int y)
    {
        if(x==y)
        {
            delete kore;
            kore=0;
            return;
        }
        int mid=x+y>>1;
        if(p<=mid) free(kore->l,x,mid);
        else free(kore->r,mid+1,y);
        if(!kore->l&&!kore->r)
        {
            delete kore;
            kore=0;
            return;
        }
        kore->max=__max(safe(kore->l,max),safe(kore->r,max));
        kore->sum=safe(kore->l,sum)+safe(kore->r,sum);
    }
    void getsum(int x,int y)
    {
        if(ll<=x&&y<=rr)
        {
            v+=sum;
            return;
        }
        int mid=x+y>>1;
        if(ll<=mid&&l) l->getsum(x,mid);
        if(mid<rr&&r) r->getsum(mid+1,y);
    }
    void getmax(int x,int y)
    {
        if(ll<=x&&y<=rr)
        {
            v=__max(v,max);
            return;
        }
        int mid=x+y>>1;
        if(ll<=mid&&l) l->getmax(x,mid);
        if(mid<rr&&r) r->getmax(mid+1,y);
    }
}*S,*T,*F[size];
struct ar
{
    node* rt;
    ar():rt(0){}
    void change(int pos,int val)
    {
        if(!rt) rt=new node;
        p=pos;v=val;
        rt->change(1,n);
    }
    void free(int pos)
    {
        p=pos;
        node::free(rt,1,n);
    }
    int getsum(int l,int r)
    {
        ll=l;rr=r;v=0;
        rt->getsum(1,n);
        return v;
    }
    int getmax(int l,int r)
    {
        ll=l;rr=r;v=0;
        rt->getmax(1,n);
        return v;
    }
}a[gm];
int tp=-1;
inline void* node::operator new(size_t)
{
    if(~tp) return F[tp--];
    if(S==T)
    {
        S=new node[size];
        T=S+size;
        memset(S,0,sizeof(node[size]));
    }
    return S++;
}

inline void node::operator delete(void* p)
{
    if(tp==size-1) ::delete (node*)p;
    else
    {
        memset(p,0,sizeof(node));
        F[++tp]=(node*)p;
    }
}

int w[gm],c[gm];
struct e
{
    int t;
    e *n;
    e(int t,e *n):t(t),n(n){}
}*f[gm];
#define link(a,b) f[a]=new e(b,f[a])
int sz[gm],fat[gm],son[gm],dpt[gm];
void dfs1(int x)
{
    sz[x]=1;
    int maxs=0,maxw=0,y;
    for(e *i=f[x];i;i=i->n)
    {
        y=i->t;
        if(fat[x]==y) continue;
        fat[y]=x;
        dpt[y]=dpt[x]+1;
        dfs1(y);
        sz[x]+=sz[y];
        if(sz[y]>maxw)
        maxs=y,maxw=sz[y];
    }
    son[x]=maxs;
}
int pos[gm],top[gm],ct=0;
void dfs2(int x)
{
    pos[x]=++ct;
    a[c[x]].change(ct,w[x]);
    top[x]=x==son[fat[x]]?top[fat[x]]:x;
    if(son[x]) dfs2(son[x]);
    for(e *i=f[x];i;i=i->n)
    {
        if(fat[x]==i->t||son[x]==i->t) continue;
        dfs2(i->t);
    }
}
char o[3];
int z;
#define swap(x,y) z=x,x=y,y=z
int getsum(int x,int y)
{
    int res=0;
    ar& kre=a[c[x]];
    while(top[x]!=top[y])
    {
        if(dpt[top[x]]<dpt[top[y]])
        swap(x,y);
        res+=kre.getsum(pos[top[x]],pos[x]);
        x=fat[top[x]];
    }
    if(dpt[x]>dpt[y])
    swap(x,y);
    return res+kre.getsum(pos[x],pos[y]);
}
int getmax(int x,int y)
{
    int res=0;
    ar& kre=a[c[x]];
    while(top[x]!=top[y])
    {
        if(dpt[top[x]]<dpt[top[y]])
        swap(x,y);
        res=__max(res,kre.getmax(pos[top[x]],pos[x]));
        x=fat[top[x]];
    }
    if(dpt[x]>dpt[y])
    swap(x,y);
    return __max(res,kre.getmax(pos[x],pos[y]));
}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
    scanf("%d%d",w+i,c+i);
    int s1,s2;
    for(int i=1;i<n;i++)
    scanf("%d%d",&s1,&s2),link(s1,s2),link(s2,s1);
    int rt=114514%n+1;
    dfs1(rt);
    dfs2(rt);
    for(int i=1;i<=q;i++)
    {
        scanf("%s%d%d",o,&s1,&s2);
        switch(o[1])
        {
            case 'C':
                a[c[s1]].free(pos[s1]);
                //a[c[s1]].change(pos[s1],0);
                c[s1]=s2;
                a[s2].change(pos[s1],w[s1]);
                break;
            case 'W':
                a[c[s1]].change(pos[s1],s2);
                w[s1]=s2;
                break;
            case 'S':
                printf("%d\n",getsum(s1,s2));
                break;
            case 'M':
                printf("%d\n",getmax(s1,s2));
                break;
        }
    }
    return 0;
}
### 树链剖分的适用场景与使用方法 树链剖分是一种高效的数据结构,用于处理树上的路径查询和修改问题。它通过将树分解为若干条不相交的链来优化复杂度,使得许多原本需要 \(O(n)\) 时间的操作可以在 \(O(\log n)\) 时间内完成[^1]。 #### 1. 树链剖分的核心思想 树链剖分的核心在于将树分割成若干条链,这些链可以拼接成树上的任意路径。常见的树链剖分方法包括重链剖分和长链剖分。其中,重链剖分是最常用的一种方法,其基本原理是:对于每个节点,选择其所有子节点中包含节点数最多的子节点作为重儿子,连接重儿子的边称为重边,由重边构成的链称为重链[^2]。 #### 2. 树链剖分的适用场景 树链剖分适用于以下场景: - **路径查询**:例如,求解树上两点之间的最大值、最小值或和等问题。 - **路径修改**:例如,对树上某条路径上的所有节点进行加法或乘法操作。 - **子树查询**:例如,求解某个节点的子树中的最大值、最小值或和等问题。 - **动态维护**:当树的结构或节点属性发生变化时,树链剖分结合线段树等数据结构可以高效地维护这些变化。 #### 3. 树链剖分的应用方法 以下是树链剖分的基本应用步骤: ```python # 树链剖分的实现示例(Python) from collections import defaultdict, deque class TreeChainDecomposition: def __init__(self, n): self.n = n self.adj = defaultdict(list) self.parent = [0] * (n + 1) self.depth = [0] * (n + 1) self.size = [0] * (n + 1) self.heavy = [0] * (n + 1) self.top = [0] * (n + 1) self.pos = [0] * (n + 1) self.rpos = [0] * (n + 1) self.cnt = 0 def add_edge(self, u, v): self.adj[u].append(v) self.adj[v].append(u) def dfs1(self, u, p): self.parent[u] = p self.size[u] = 1 max_subtree = -1 for v in self.adj[u]: if v != p: self.depth[v] = self.depth[u] + 1 self.dfs1(v, u) self.size[u] += self.size[v] if self.size[v] > max_subtree: max_subtree = self.size[v] self.heavy[u] = v def dfs2(self, u, t): self.top[u] = t self.pos[u] = self.cnt self.rpos[self.cnt] = u self.cnt += 1 if self.heavy[u] != 0: self.dfs2(self.heavy[u], t) for v in self.adj[u]: if v != self.parent[u] and v != self.heavy[u]: self.dfs2(v, v) # 示例:初始化并构建树 n = 5 tree = TreeChainDecomposition(n) edges = [(1, 2), (1, 3), (2, 4), (2, 5)] for u, v in edges: tree.add_edge(u, v) tree.dfs1(1, 0) tree.dfs2(1, 1) ``` 上述代码实现了树链剖分的基本框架,包括深度优先搜索(DFS)和重链划分。 #### 4. 实际应用案例 以 bzoj3252 为例,题目要求在树状结构中求解路径的最大价值和。这种问题可以通过树链剖分结合线段树或树状数组来解决。具体步骤如下: - 使用树链剖分将树划分为若干条链。 - 对每条链建立线段树或其他支持快速区间查询和修改的数据结构。 - 在查询或修改时,将路径拆分为若干条链,并分别在线段树上进行操作[^3]。 #### 5. 注意事项 - 树链剖分的时间复杂度通常为 \(O(n \log n)\),适合处理大规模数据。 - 在实际应用中,需要根据问题的具体需求选择合适的剖分方式(如重链剖分或长链剖分)。 - 结合其他数据结构(如线段树、树状数组)可以进一步提升效率。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值