bzoj2243 [SDOI2011]染色 树链剖分

本文介绍了一种利用树链剖分和线段树解决特定树形结构问题的方法,具体为处理节点间的染色操作及颜色段查询。通过对树进行预处理并建立辅助数据结构,可以高效地完成多次修改和查询任务。

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

Description


给定一棵有n个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),
如“112221”由3段组成:“11”、“222”和“1”。
请你写一个程序依次完成这m个操作。

“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;
“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。

HINT


数N<=10^5,操作数M<=10^5,所有的颜色C为整数且在[0, 10^9]之间。

Solution


很自然地就想到了树链剖分,线段树维护左边第一个颜色,右边第一个颜色,以及这一段颜色的数量

考虑到在查询重边的时候会有相邻颜色相同的一段,那么我们每做完一条重边就判断它的父亲是否和重边中较浅的点颜色相同

Code


#include <stdio.h>
#include <string.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
const int N=200005;
const int E=500005;
struct edge{int x,y,next;}e[E];
int lazy[N<<2],tot[N<<2],lc[N<<2],rc[N<<2];
int size[N],pos[N],dep[N],bl[N],fa[N],cnt=0;
int ls[N],edCnt=0;
int c[N];
int n,m;
int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}
void addEdge(int x,int y) {
    e[++edCnt]=(edge){x,y,ls[x]}; ls[x]=edCnt;
    e[++edCnt]=(edge){y,x,ls[y]}; ls[y]=edCnt;
}
void swap(int &x,int &y) {x^=y; y^=x; x^=y;}
void buildTree(int now,int tl,int tr) {
    if (tl==tr) {return ;}
    int mid=(tl+tr)>>1;
    buildTree(now<<1,tl,mid);
    buildTree(now<<1|1,mid+1,tr);
}
void dfs1(int now) {
    size[now]=1;
    for (int i=ls[now];i;i=e[i].next) {
        if (e[i].y==fa[now]) {continue;}
        dep[e[i].y]=dep[now]+1;
        fa[e[i].y]=now;
        dfs1(e[i].y);
        size[now]+=size[e[i].y];
    }
}
void dfs2(int now,int up) {
    pos[now]=++cnt;
    bl[now]=up;
    int mx=0;
    for (int i=ls[now];i;i=e[i].next) {
        if (dep[e[i].y]>dep[now]&&size[e[i].y]>size[mx]) {mx=e[i].y;}
    }
    if (!mx) {return ;}
    dfs2(mx,up);
    for (int i=ls[now];i;i=e[i].next) {
        if (dep[e[i].y]<dep[now]||e[i].y==mx) {continue;}
        dfs2(e[i].y,e[i].y);
    }
}
void push_down(int now) {
    if (!lazy[now]) {return ;}
    lazy[now<<1]=lazy[now<<1|1]=lazy[now];
    lc[now<<1]=rc[now<<1]=lc[now<<1|1]=rc[now<<1|1]=lazy[now];
    tot[now<<1]=tot[now<<1|1]=1;
    lazy[now]=0;
}
void push_up(int now) {
    tot[now]=tot[now<<1]+tot[now<<1|1];
    if (rc[now<<1]==lc[now<<1|1]) tot[now]-=1;
    lc[now]=lc[now<<1];
    rc[now]=rc[now<<1|1];
}
void modify(int now,int tl,int tr,int l,int r,int v) {
    push_down(now);
    if (tl==l&&tr==r) {
        lazy[now]=v;
        tot[now]=1;
        lc[now]=rc[now]=v;
        return ;
    }
    int mid=(tl+tr)>>1;
    if (r<=mid) {
        modify(now<<1,tl,mid,l,r,v);
    } else if (l>mid) {
        modify(now<<1|1,mid+1,tr,l,r,v);
    } else {
        modify(now<<1,tl,mid,l,mid,v);
        modify(now<<1|1,mid+1,tr,mid+1,r,v);
    }
    push_up(now);
}
int query(int now,int tl,int tr,int l,int r) {
    push_down(now);
    if (tl==l&&tr==r) {return tot[now];}
    int mid=(tl+tr)>>1;
    if (r<=mid) {return query(now<<1,tl,mid,l,r);}
    else if (l>mid) {return query(now<<1|1,mid+1,tr,l,r);}
    else {
        int qx=query(now<<1,tl,mid,l,mid);
        int qy=query(now<<1|1,mid+1,tr,mid+1,r);
        int ret=qx+qy;
        if (rc[now<<1]==lc[now<<1|1]) ret-=1;
        return ret;
    }
}
int query_color(int now,int tl,int tr,int pos) {
    push_down(now);
    if (tl==tr) {return lc[now];}
    int mid=(tl+tr)>>1;
    if (pos<=mid) {return query_color(now<<1,tl,mid,pos);}
    else {return query_color(now<<1|1,mid+1,tr,pos);}
}
int get_ans(int x,int y) {
    int ret=0;
    while (bl[x]!=bl[y]) {
        if (dep[bl[x]]<dep[bl[y]]) {swap(x,y);}
        ret+=query(1,1,n,pos[bl[x]],pos[x]);
        int color1=query_color(1,1,n,pos[bl[x]]);
        int color2=query_color(1,1,n,pos[fa[bl[x]]]);
        if (color1==color2) {ret-=1;}
        x=fa[bl[x]];
    }
    if (dep[x]>dep[y]) {swap(x,y);}
    ret+=query(1,1,n,pos[x],pos[y]);
    return ret;
}
void solve(int x,int y,int z) {
    while (bl[x]!=bl[y]) {
        if (dep[bl[x]]<dep[bl[y]]) {swap(x,y);}
        modify(1,1,n,pos[bl[x]],pos[x],z);
        x=fa[bl[x]];
    }
    if (dep[x]>dep[y]) {swap(x,y);}
    modify(1,1,n,pos[x],pos[y],z);
}
int main(void) {
    n=read();
    m=read();
    rep(i,1,n) {c[i]=read();}
    rep(i,2,n) {
        int x=read();
        int y=read();
        addEdge(x,y);
    }
    dfs1(1);
    dfs2(1,1);
    rep(i,1,n) {modify(1,1,n,pos[i],pos[i],c[i]);}
    while (m--) {
        char opt[5]; scanf("%s",opt);
        if (opt[0]=='Q') {
            int x=read();
            int y=read();
            int prt=get_ans(x,y);
            printf("%d\n",prt);
        } else if (opt[0]=='C') {
            int x=read();
            int y=read();
            int z=read();
            solve(x,y,z);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值