• Luogu P2486 [SDOI2011]染色(树链剖分+线段树合并)


    Luogu P2486 [SDOI2011]染色

    题面

    题目描述

    输入输出格式

    输入格式:

    输出格式:

    对于每个询问操作,输出一行答案。

    输入输出样例

    输入样例:

    6 5
    2 2 1 2 1 1
    1 2
    1 3
    2 4
    2 5
    2 6
    Q 3 5
    C 2 1 1
    Q 3 5
    C 5 1 2
    Q 3 5
    

    输出样例:

    3
    1
    2
    

    说明:

    思路

    好久没打树剖了,今天和水星爹爹 (solo) 这道题的时候输掉了(你爹爹还是你爹爹) (qwq)

    进入正题。早在成都的时候 (czk) 巨佬就一直推荐我写这道题。

    这题对细节的考虑非常多。 --czk

    不过我一直懒得开(其实是自己太蒻了),后来开题也都是写挂了。其实这题并不难,很板,主要就是考虑如何用线段树/树链剖分维护区间的颜色以及颜色段数。

    对于一个线段树节点,维护以下内容:

    • (l,r:) 该节点维护的区间范围 ([l,r])
    • (maxl,maxr:) 该节点所维护的区间的最左边节点颜色和最右边节点颜色;
    • (data:) 该节点所维护区间的颜色段数;
    • (tag:) 即懒惰标记,初始化时 (tag=0)

    我们不妨先解决这样一个问题:

    给定一个序列(颜色串),每次修改一个区间 ([l,r]) 使之颜色(权值)全部变为一个数,或询问一个区间 ([l,r]) 中的颜色段数是多少。

    那么有了上面约定的节点储存信息,这样的询问是可合并的,很方便用线段树解决。

    具体来说,我们的线段树操作这样做:

    struct SegmentTree
    {
        int l,r,maxl,maxr,data,tag;
        #define l(a) tree[a].l
        #define r(a) tree[a].r
        #define ml(a) tree[a].maxl
        #define mr(a) tree[a].maxr
        #define d(a) tree[a].data
        #define t(a) tree[a].tag
        void init(){l=r=maxl=maxr=data=tag=0;}
    }tree[MAXN<<2];//zcysky的线段树写法qwq
    inline SegmentTree merge(SegmentTree x,SegmentTree y)//重点(敲黑板):线段树合并。将左边的x和右边的y合并起来
    {
        if(!x.data) return y;
        if(!y.data) return x;
        SegmentTree re;
        re.init();
        re.maxl=x.maxl,re.maxr=y.maxr,re.data=x.data+y.data;
        if(x.maxr==y.maxl) re.data--;
        return re;
    }
    inline void update(int p)//上传更新
    {
        ml(p)=ml(p<<1),mr(p)=mr(p<<1|1);
        d(p)=d(p<<1)+d(p<<1|1);
        if(mr(p<<1)==ml(p<<1|1)) d(p)--;//该区间中间,即左区间的最右端和右区间的最左端相等时,颜色段数--
    }
    inline void pushdown(int p)//标记下传
    {
        if(t(p))
        {
            ml(p<<1)=mr(p<<1)=t(p<<1)=t(p);
            ml(p<<1|1)=mr(p<<1|1)=t(p<<1|1)=t(p);
            d(p<<1)=d(p<<1|1)=1;
            t(p)=0;
        }
    }
    void build(int p,int ll,int rr)//建树
    {
        l(p)=ll,r(p)=rr;
        if(ll==rr)
        {
            ml(p)=mr(p)=b[ll];
            d(p)=1;
            return ;
        }
        int mid=(l(p)+r(p))>>1;
        build(p<<1,ll,mid);
        build(p<<1|1,mid+1,rr);
        update(p);
    }
    void change(int p,int ll,int rr,int k)//修改操作:将[ll,rr]区间的权值修改为k
    {
        if(ll<=l(p)&&r(p)<=rr)
        {
            d(p)=1,ml(p)=mr(p)=t(p)=k;
            return ;
        }
        pushdown(p);
        int mid=(l(p)+r(p))>>1;
        if(mid>=ll) change(p<<1,ll,rr,k);
        if(mid<rr) change(p<<1|1,ll,rr,k);
        update(p);
    }
    SegmentTree ask(int p,int ll,int rr)//询问操作,直接返回一个线段树,方便合并
    {
        if(ll<=l(p)&&r(p)<=rr) return tree[p];
        pushdown(p);
        int mid=(l(p)+r(p))>>1;
        if(mid>=rr) return ask(p<<1,ll,rr);
        else if(mid<ll) return ask(p<<1|1,ll,rr);
        else return merge(ask(p<<1,ll,rr),ask(p<<1|1,ll,rr));
    }
    

    代码的核心在于线段树合并的函数 updatemerge 。直接在节点上操作,可以省去很多麻烦。

    再把问题转移到树上来。考虑关于 (u,v) 两节点之间颜色段数的询问,其实就相当于将 (u) 往上跳到 (LCA(u,v)) 所合并出的线段树与 (v) 往上跳到 (LCA(u,v)) 所合并出的线段树进行合并。在 (u,v) 分别向上跳时,每次合并时,按照树链剖分的节点命名规律,先处理的是线段树中的右边部分,再处理左边部分,所以要将新处理的部分与已经处理过的部分进行合并。当 (u,v) 都跳到了同一条链上时,因为 (LCA(u,v)) 处储存的是需要合并的两个线段树节点的最左端颜色,所以就不能再用之前的 merge() 函数,而要特判从 (u) 开始合并出的线段树 与从 (v) 开始合并出的线段树的最左端点 maxl 是否相同。代码如下:

    int x=read(),y=read();//x,y即上述的u,v
    SegmentTree xx,yy;//xx为从x开始合并出的线段树,yy亦然
    xx.init(),yy.init();//一开始要清空!
    while(st[x]!=st[y])
    {
        if(dep[st[x]]<dep[st[y]]) swap(x,y),swap(xx,yy);//交换x,y时也要记得交换xx和yy
        xx=merge(ask(1,id[st[x]],id[x]),xx);
        x=fa[st[x]];
    }
    if(id[x]>id[y]) swap(x,y),swap(xx,yy);
    yy=merge(ask(1,id[x],id[y]),yy);
    int ans=xx.data+yy.data;
    if(xx.maxl==yy.maxl) ans--;//特判
    printf("%d
    ",ans);
    

    其他操作就和普通的树剖板子很类似了,在这里就不做过多讲解,详情见代码。

    AC代码

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=1e5+5;
    int n,m,a[MAXN],b[MAXN],fa[MAXN],dep[MAXN],sz[MAXN],son[MAXN];
    int cnt,top[MAXN],to[MAXN<<1],nex[MAXN<<1];
    int tot,id[MAXN],st[MAXN];
    struct SegmentTree
    {
        int l,r,maxl,maxr,data,tag;
        #define l(a) tree[a].l
        #define r(a) tree[a].r
        #define ml(a) tree[a].maxl
        #define mr(a) tree[a].maxr
        #define d(a) tree[a].data
        #define t(a) tree[a].tag
        void init(){l=r=maxl=maxr=data=tag=0;}
    }tree[MAXN<<2];
    inline int read()
    {
        int re=0;
        char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
        return re;
    }
    inline char readc()
    {
        char ch=getchar();
        while(!isalpha(ch)) ch=getchar();
        return ch;
    }
    void dfs1(int now)
    {
        for(int i=top[now];i;i=nex[i])
        {
            if(to[i]==fa[now]) continue;
            fa[to[i]]=now,sz[to[i]]=1,dep[to[i]]=dep[now]+1;
            dfs1(to[i]);
            sz[now]+=sz[to[i]];
            if(sz[to[i]]>sz[son[now]]) son[now]=to[i];
        }
    }
    void dfs2(int now,int line_top)
    {
        id[now]=++tot,b[tot]=a[now],st[now]=line_top;
        if(!son[now]) return ;
        dfs2(son[now],line_top);
        for(int i=top[now];i;i=nex[i])
        {
            if(to[i]==fa[now]||to[i]==son[now]) continue;
            dfs2(to[i],to[i]);
        }
    }
    inline SegmentTree merge(SegmentTree x,SegmentTree y)
    {
        if(!x.data) return y;
        if(!y.data) return x;
        SegmentTree re;
        re.init();
        re.maxl=x.maxl,re.maxr=y.maxr,re.data=x.data+y.data;
        if(x.maxr==y.maxl) re.data--;
        return re;
    }
    inline void update(int p)
    {
        ml(p)=ml(p<<1),mr(p)=mr(p<<1|1);
        d(p)=d(p<<1)+d(p<<1|1);
        if(mr(p<<1)==ml(p<<1|1)) d(p)--;
    }
    inline void pushdown(int p)
    {
        if(t(p))
        {
            ml(p<<1)=mr(p<<1)=t(p<<1)=t(p);
            ml(p<<1|1)=mr(p<<1|1)=t(p<<1|1)=t(p);
            d(p<<1)=d(p<<1|1)=1;
            t(p)=0;
        }
    }
    void build(int p,int ll,int rr)
    {
        l(p)=ll,r(p)=rr;
        if(ll==rr)
        {
            ml(p)=mr(p)=b[ll];
            d(p)=1;
            return ;
        }
        int mid=(l(p)+r(p))>>1;
        build(p<<1,ll,mid);
        build(p<<1|1,mid+1,rr);
        update(p);
    }
    void change(int p,int ll,int rr,int k)
    {
        if(ll<=l(p)&&r(p)<=rr)
        {
            d(p)=1,ml(p)=mr(p)=t(p)=k;
            return ;
        }
        pushdown(p);
        int mid=(l(p)+r(p))>>1;
        if(mid>=ll) change(p<<1,ll,rr,k);
        if(mid<rr) change(p<<1|1,ll,rr,k);
        update(p);
    }
    SegmentTree ask(int p,int ll,int rr)
    {
        if(ll<=l(p)&&r(p)<=rr) return tree[p];
        pushdown(p);
        int mid=(l(p)+r(p))>>1;
        if(mid>=rr) return ask(p<<1,ll,rr);
        else if(mid<ll) return ask(p<<1|1,ll,rr);
        else return merge(ask(p<<1,ll,rr),ask(p<<1|1,ll,rr));
    }
    int main()
    {
        n=read(),m=read();
        for(int i=1;i<=n;i++) a[i]=read();
        for(int i=0;i<n-1;i++)
        {
            int x=read(),y=read();
            to[++cnt]=y,nex[cnt]=top[x],top[x]=cnt;
            to[++cnt]=x,nex[cnt]=top[y],top[y]=cnt;
        }
        fa[1]=sz[1]=dep[1]=1;
        dfs1(1);
        dfs2(1,1);
        build(1,1,n);
        while(m--)
        {
            char opt=readc();
            if(opt=='C')
            {
                int x=read(),y=read(),z=read();
                while(st[x]!=st[y])
                {
                    if(dep[st[x]]<dep[st[y]]) swap(x,y);
                    change(1,id[st[x]],id[x],z);
                    x=fa[st[x]];
                }
                if(id[x]>id[y]) swap(x,y);
                change(1,id[x],id[y],z);
            }
            else if(opt=='Q')
            {
                int x=read(),y=read();
                SegmentTree xx,yy;
                xx.init(),yy.init();
                while(st[x]!=st[y])
                {
                    if(dep[st[x]]<dep[st[y]]) swap(x,y),swap(xx,yy);
                    xx=merge(ask(1,id[st[x]],id[x]),xx);
                    x=fa[st[x]];
                }
                if(id[x]>id[y]) swap(x,y),swap(xx,yy);
                yy=merge(ask(1,id[x],id[y]),yy);
                int ans=xx.data+yy.data;
                if(xx.maxl==yy.maxl) ans--;
                printf("%d
    ",ans);
            }
        }
        return 0;
    }
    
    
  • 相关阅读:
    CSS中:display:none与visible:hidden的区别
    $(function(){})和$(document).ready(function(){}) 的用法
    JavaScript 全选函数的实现
    HTML:关于a标签的target属性
    CSS:给 input 中 type="text" 设置CSS样式
    JavaScript中“javascript:void(0) ”是什么意思
    Oracle数据库——数据库安全性管理
    使用JavaScript根据从后台获取来的数据打开一个新的页面
    java reflect反射---Java高级开发必须懂的
    Java 类加载机制
  • 原文地址:https://www.cnblogs.com/coder-Uranus/p/9714011.html
Copyright © 2020-2023  润新知