BZOJ 4127 Abs 树链剖分

博客介绍了BZOJ 4127题目的解决方案,利用树链剖分和线段树来处理树上的权值操作。题目要求对树上的点进行加操作和求绝对值之和,通过树链剖分优化,只需在线段树中维护区间最大负数,避免了超时问题。

题目大意:给定一棵树,每个点有一个整数权值(可以是负数),要求支持两种操作:
1.链上加
2.链上绝对值之和

由于加的数保证非负,因此一个负数变成一个正数最多有n
树链剖分,在线段树中维护一下区间最大负数即可
不知道为何 写了两个线段树就TLE 把两个线段树合并成一个就7s过了

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
using namespace std;

struct Segtree{
    Segtree *ls,*rs;
    long long sum,mark;
    int cnt;//区间中负数的个数
    pair<int,int> max_pos;
    void* operator new (size_t)
    {
        static Segtree mempool[M<<1],*C=mempool;
        return C++;
    }
    void Add(int x,int y,long long val)
    {
        sum+=(y-x+1-2*cnt)*val;
        mark+=val;
        if(max_pos.first!=(signed)0xefefefef)
            max_pos.first+=val;
    }
    void Push_Up(int x,int y)
    {
        sum=ls->sum+rs->sum;
        cnt=ls->cnt+rs->cnt;
        max_pos=max(ls->max_pos,rs->max_pos);
    }
    void Push_Down(int x,int y)
    {
        int mid=x+y>>1;
        if(mark)
        {
            ls->Add(x,mid,mark);
            rs->Add(mid+1,y,mark);
            mark=0;
        }
    }
    void Build_Tree(int x,int y,int a[])
    {
        int mid=x+y>>1;
        if(x==y)
        {
            if(a[mid]<0)
                max_pos=pair<int,int>(a[mid],mid),cnt=1;
            else
                max_pos=pair<int,int>(0xefefefef,0);
            sum=abs(a[mid]);
            return ;
        }
        (ls=new Segtree)->Build_Tree(x,mid,a);
        (rs=new Segtree)->Build_Tree(mid+1,y,a);
        Push_Up(x,y);
    }
    void Reverse(int x,int y,int pos)
    {
        int mid=x+y>>1;
        if(x==y)
        {
            cnt=0;
            sum=-sum;
            max_pos=pair<int,int>(0xefefefef,0);
            return ;
        }
        Push_Down(x,y);
        if(pos<=mid)
            ls->Reverse(x,mid,pos);
        else
            rs->Reverse(mid+1,y,pos);
        Push_Up(x,y);
    }
    void Add(int x,int y,int l,int r,long long val)
    {
        int mid=x+y>>1;
        if(x==l&&y==r)
        {
            Add(x,y,val);
            return ;
        }
        Push_Down(x,y);
        if(r<=mid)
            ls->Add(x,mid,l,r,val);
        else if(l>mid)
            rs->Add(mid+1,y,l,r,val);
        else
            ls->Add(x,mid,l,mid,val) , rs->Add(mid+1,y,mid+1,r,val) ;
        Push_Up(x,y);
    }
    long long Query(int x,int y,int l,int r)
    {
        int mid=x+y>>1;
        if(x==l&&y==r)
            return sum;
        Push_Down(x,y);
        if(r<=mid)
            return ls->Query(x,mid,l,r);
        if(l>mid)
            return rs->Query(mid+1,y,l,r);
        return ls->Query(x,mid,l,mid) + rs->Query(mid+1,y,mid+1,r) ;
    }
    pair<int,int> Get_Pos(int x,int y,int l,int r)
    {
        int mid=x+y>>1;
        if(x==l&&y==r)
            return max_pos;
        Push_Down(x,y);
        if(r<=mid)
            return ls->Get_Pos(x,mid,l,r);
        if(l>mid)
            return rs->Get_Pos(mid+1,y,l,r);
        return max( ls->Get_Pos(x,mid,l,mid) , rs->Get_Pos(mid+1,y,mid+1,r) );
    }
}*tree=new Segtree;

struct abcd{
    int to,next;
}table[M<<1];
int head[M],tot;

int n,m,a[M];
int fa[M],son[M],dpt[M],size[M];
int pos[M],top[M],_a[M];

void Add(int x,int y)
{
    table[++tot].to=y;
    table[tot].next=head[x];
    head[x]=tot;
}

void DFS1(int x)
{
    int i;
    dpt[x]=dpt[fa[x]]+1;
    size[x]=1;
    for(i=head[x];i;i=table[i].next)
        if(table[i].to!=fa[x])
        {
            fa[table[i].to]=x;
            DFS1(table[i].to);
            size[x]+=size[table[i].to];
            if(size[table[i].to]>size[son[x]])
                son[x]=table[i].to;
        }
}

void DFS2(int x)
{
    static int T;
    int i;
    _a[pos[x]=++T]=a[x];
    if(son[fa[x]]==x)
        top[x]=top[fa[x]];
    else
        top[x]=x;
    if(son[x])
        DFS2(son[x]);
    for(i=head[x];i;i=table[i].next)
        if(table[i].to!=fa[x]&&table[i].to!=son[x])
            DFS2(table[i].to);
}

void Add(int x,int y,int z)
{
    int fx=top[x],fy=top[y];
    while(fx!=fy)
    {
        if(dpt[fx]<dpt[fy])
            swap(fx,fy),swap(x,y);
        while(1)
        {
            pair<int,int> temp=tree->Get_Pos(1,n,pos[fx],pos[x]);
            if(temp.first+z>=0)
                tree->Reverse(1,n,temp.second);
            else
                break;
        }
        tree->Add(1,n,pos[fx],pos[x],z);
        x=fa[fx];fx=top[x];
    }
    if(dpt[x]<dpt[y])
        swap(x,y);
    while(1)
    {
        pair<int,int> temp=tree->Get_Pos(1,n,pos[y],pos[x]);
        if(temp.first+z>=0)
            tree->Reverse(1,n,temp.second);
        else
            break;
    }
    tree->Add(1,n,pos[y],pos[x],z);
}

long long Query(int x,int y)
{
    int fx=top[x],fy=top[y];
    long long re=0;
    while(fx!=fy)
    {
        if(dpt[fx]<dpt[fy])
            swap(fx,fy),swap(x,y);
        re+=tree->Query(1,n,pos[fx],pos[x]);
        x=fa[fx];fx=top[x];
    }
    if(dpt[x]<dpt[y])
        swap(x,y);
    return re+tree->Query(1,n,pos[y],pos[x]);
}

int main()
{
    int i,p,x,y,z;
    cin>>n>>m;
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        Add(x,y);Add(y,x);
    }
    DFS1(1);DFS2(1);
    tree->Build_Tree(1,n,_a);
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d",&p,&x,&y);
        if(p==1)
        {
            scanf("%d",&z);
            if(z) Add(x,y,z);
        }
        else
        {
            #ifdef PoPoQQQ
                printf("%I64d\n",Query(x,y));
            #else
                printf("%lld\n",Query(x,y));
            #endif
        }
    }
    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)\),适合处理大规模数据。 - 在实际应用中,需要根据问题的具体需求选择合适的剖分方式(如重链剖分或长链剖分)。 - 结合其他数据结构(如线段树、树状数组)可以进一步提升效率。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值