【bzoj2243】【树链剖分】【线段树】SDOI2011染色

本文介绍了一种解决树形结构中区间染色及查询问题的方法,通过树剖技术结合线段树来高效处理节点间路径上的颜色修改与查询操作。
  • 题目大意:
    给定一棵有N 个节点的无根树和 M 个操作,操作有2类:
    1. 将节点A 到节点B路径上所有点都染成颜色 C
    2. 询问节点A到节点B路径上的颜色段数量(连续相同颜色被认为是同一段),如”112221”由3段组成:”11”.”222”和”1”。
      请你写一个程序依次完成这 M 个操作。
  • 数据范围:
    这里写图片描述
  • 题解:
    对树上的路径进行操作, 区间覆盖,区间查询,一瞅就可以树剖,剖完之后用线段树维护区间某种颜色出现的个数col,区间左端点颜色lcol,区间右端点颜色rcol
  • 合并时满足:
    p>col=p>lch>col+p>rch>col(p>lch>rcol==p>rch>lcol)
  • 注意延重链向上爬的时候也要维护lcol和rcol进行统计答案
    直接上代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#define MAXN 100001
using namespace std;
vector<int> e[MAXN];
int n,m;
int dep[MAXN],size[MAXN],hson[MAXN],fa[MAXN];
void dfs1(int u,int f,int d){
    size[u]=1,fa[u]=f,dep[u]=d;
    for(vector<int>::iterator it=e[u].begin();it!=e[u].end();it++){
        if(*it==f) continue;
        dfs1(*it,u,d+1);
        size[u]+=size[*it];
        if(size[*it]>size[hson[u]]) hson[u]=*it;
    }
}
int cnt[MAXN],top[MAXN],T,bel[MAXN];
void dfs2(int u,int tp){
    bel[T]=u,cnt[u]=T++,top[u]=tp;
    if(hson[u]) dfs2(hson[u],tp);
    for(vector<int>::iterator it=e[u].begin();it!=e[u].end();it++){
        if(*it==hson[u]||*it==fa[u]) continue;
        dfs2(*it,*it);
    }
}
struct node{
    int lnum,rnum,kind,l,r,mid,lzy;
    node *lch,*rch;
    node(){}
    node(int a,int b,int c):l(a),r(b),mid(c){lnum=rnum=kind=lzy=0;lch=rch=NULL;}
}*root;
int C[MAXN];
void push_down(node *p){
    p->lch->lzy=p->rch->lzy=p->lzy;
    p->lch->lnum=p->lch->rnum=p->lzy;
    p->lch->kind=1;
    p->rch->lnum=p->rch->rnum=p->lzy;
    p->rch->kind=1;
    p->lzy=0;
}
void push_up(node *p){
    p->kind=p->lch->kind+p->rch->kind;
    if(p->lch->rnum==p->rch->lnum)
        p->kind--;
    p->lnum=p->lch->lnum;
    p->rnum=p->rch->rnum;
}
node *build(int left,int right){
    node *p=new node(left,right,(left+right)>>1);
    if(right-left==1){
        p->kind=1;
        p->lnum=p->rnum=C[bel[left]];
        return p;
    }
    p->lch=build(left,p->mid);
    p->rch=build(p->mid,right);
    push_up(p);
    return p;
}
void change(node *p,int left,int right,int col){
    if(p->l==left&&p->r==right){
        p->kind=1,p->lnum=p->rnum=col;
        p->lzy=col;
        return;
    }
    if(p->lzy)
        push_down(p);
    if(right<=p->mid)
        change(p->lch,left,right,col);
    else if(left>=p->mid)
        change(p->rch,left,right,col);
    else{
        change(p->lch,left,p->mid,col);
        change(p->rch,p->mid,right,col);
    }
    push_up(p);
}
struct Q{
    int sum,lcol,rcol;
    Q(int a,int b,int c):sum(a),lcol(b),rcol(c){}
    Q(){}
};
Q query(node *p,int left,int right){
    if(p->l==left&&p->r==right)
        return Q(p->kind,p->lnum,p->rnum);
    if(p->lzy)
        push_down(p);
    if(right<=p->mid)
        return query(p->lch,left,right);
    else if(left>=p->mid)
        return query(p->rch,left,right);
    else{
        Q a=query(p->lch,left,p->mid);
        Q b=query(p->rch,p->mid,right);
        if(a.rcol==b.lcol)
            return Q(a.sum+b.sum-1,a.lcol,b.rcol);
        else
            return Q(a.sum+b.sum,a.lcol,b.rcol);
    }
}
void changeans(int u,int v,int c){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        change(root,cnt[top[u]],cnt[u]+1,c);
        u=fa[top[u]];
    }
    if(cnt[u]>cnt[v]) swap(u,v);
    change(root,cnt[u],cnt[v]+1,c);
}
int getans(int s,int t){
    pair<int,int> u,v;
    u=make_pair(s,0);
    v=make_pair(t,1);
    int lc[2]={-1};
    int ans[2]={0};
    while(top[u.first]!=top[v.first]){
        if(dep[top[u.first]]<dep[top[v.first]]) swap(u,v);
        Q temp=query(root,cnt[top[u.first]],cnt[u.first]+1);
        ans[u.second]=ans[u.second]+temp.sum-(lc[u.second]==temp.rcol);
        lc[u.second]=temp.lcol;
        u.first=fa[top[u.first]];
    }
    if(cnt[u.first]>cnt[v.first]) swap(u,v);
    Q te=query(root,cnt[u.first],cnt[v.first]+1);
    int anssum=ans[0]+ans[1]+te.sum-(lc[u.second]==te.lcol)-(lc[v.second]==te.rcol);
    return anssum;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&C[i]);
    for(int i=1;i<n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        e[a].push_back(b);
        e[b].push_back(a);
    }
    dfs1(1,-1,0);
    dfs2(1,1);
    root=build(0,T);
    for(int i=1;i<=m;i++){
        char a[2];
        scanf("%s",a);
        if(a[0]=='C'){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            changeans(a,b,c);
        }
        else if(a[0]=='Q'){
            int a,b;
            scanf("%d%d",&a,&b);
            printf("%d\n",getans(a,b));
        }
    }
    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、付费专栏及课程。

余额充值