洛谷 P2486 [SDOI2011]染色(LCT)

很经典的一道题目,这里介绍LCT的做法。
对splay的每个结点维护当前以该结点为根的子树内第一个结点和最后一个结点(即对应区间两端)的颜色,以及该子树内颜色段数。pushup合并的时候,如果合并的两个区间相邻部分的颜色不同,新区间的颜色段数就是两个区间的和,如果相同就-1。修改就打lazy标记。注意打标记要同时更新,不能等pushdown再更新。另外翻转的时候还要交换区间两端颜色。

似乎树剖更好写?

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,m;

inline int read(){
    int ret=0,sign=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') sign=-1;
        if(ch=='C'||ch=='Q') return ch;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*sign;
}

int fa[maxn],son[maxn][2],c0[maxn],c[maxn][2],sum[maxn],rev[maxn],tag[maxn];
inline bool isroot(int u){return son[fa[u]][0]!=u&&son[fa[u]][1]!=u;}
inline int getson(int u){return son[fa[u]][1]==u;}
inline void pushup(int u){
    sum[u]=1,c[u][0]=c[u][1]=c0[u];
    if(son[u][0]) sum[u]+=c0[u]==c[son[u][0]][1]?sum[son[u][0]]-1:sum[son[u][0]],c[u][0]=c[son[u][0]][0];
    if(son[u][1]) sum[u]+=c0[u]==c[son[u][1]][0]?sum[son[u][1]]-1:sum[son[u][1]],c[u][1]=c[son[u][1]][1];
}
inline void Rev(int u){rev[u]^=1,swap(son[u][0],son[u][1]),swap(c[u][0],c[u][1]);}
inline void modify(int u,int x){tag[u]=c0[u]=c[u][0]=c[u][1]=x,sum[u]=1;}
inline void pushdown(int u){
    if(rev[u]){
        rev[u]=0;
        if(son[u][0]) Rev(son[u][0]);
        if(son[u][1]) Rev(son[u][1]);
    }
    if(tag[u]){
        if(son[u][0]) modify(son[u][0],tag[u]);
        if(son[u][1]) modify(son[u][1],tag[u]);
        tag[u]=0;
    }
}
inline void rotate(int u){
    int v=fa[u],rela=getson(u);
    fa[u]=fa[v]; if(!isroot(v)) son[fa[v]][getson(v)]=u;
    son[v][rela]=son[u][rela^1]; if(son[u][rela^1]) fa[son[u][rela^1]]=v;
    fa[v]=u,son[u][rela^1]=v;
    pushup(v),pushup(u);
}
int S[maxn],top;
inline void splay(int u){
    S[top=1]=u;
    for(int t=u;!isroot(t);t=fa[t]) S[++top]=fa[t];
    while(top) pushdown(S[top--]);
    while(!isroot(u)){
        int v=fa[u];
        if(!isroot(v)) rotate(getson(u)==getson(v)?v:u);
        rotate(u);
    }
}

inline void access(int u){for(int v=0;u;v=u,u=fa[u]) splay(u),son[u][1]=v,pushup(u);}
inline void makeroot(int u){access(u),splay(u),Rev(u);}
inline void split(int u,int v){makeroot(u),access(v),splay(v);}
inline void link(int u,int v){makeroot(u),fa[u]=v;}

int main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++) c0[i]=c[i][0]=c[i][1]=read(),sum[i]=1;
    for(int i=1;i<n;i++) link(read(),read());
    for(int i=1;i<=m;i++){
        int op=read(),a=read(),b=read(),_c;
        split(a,b);
        if(op=='C') _c=read(),modify(b,_c);
        else printf("%d\n",sum[b]);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值