• P2486 [SDOI2011]染色


    传送门

    先考虑在一段序列上进行操作如何维护

    线段树

    记录每个区间的的颜色段数量

    但是区间合并时两边可能颜色相同

    所以再记录一下每个区间最左和最右的颜色

    合并时如果相邻两边颜色相同,那么颜色段数量就要减一

    然后考虑在树上操作

    直接上树剖,一样用线段树维护就好了

    询问时记录一下当前左右两边最上面的颜色

    然后跟取出的区间最下面的颜色比较一下

    如果颜色相同答案也要减1

    具体细节在代码里

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9')
        {
            if(ch=='-') f=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9')
        {
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return x*f;
    }
    
    const int N=4e5+7;
    int n,m;
    int c[N];//原树上颜色
    
    int fir[N<<2],from[N<<2],to[N<<2],cntt;//前向星存树
    inline void add(int &a,int &b)
    {
        from[++cntt]=fir[a];
        fir[a]=cntt; to[cntt]=b;
    }
    
    int fa[N],sz[N],dep[N],son[N];//以下为树剖的预处理,不解释
    void dfs1(int &x,int &f)
    {
        dep[x]=dep[f]+1; fa[x]=f; sz[x]=1;
        int mxsz=0;
        for(int i=fir[x];i;i=from[i])
        {
            if(to[i]==f) continue;
            dfs1(to[i],x); sz[x]+=sz[to[i]];
            if(sz[to[i]]>mxsz)
            {
                mxsz=sz[to[i]];
                son[x]=to[i];
            }
        }
    }
    int id[N],col[N]/*col存线段树上的颜色*/,Top[N],cnt;
    void dfs2(int &x,int &topp)
    {
        id[x]=++cnt; col[cnt]=c[x]; Top[x]=topp;
        if(!son[x]) return;
        dfs2(son[x],topp);
        for(int i=fir[x];i;i=from[i])
        {
            if(to[i]==fa[x]||to[i]==son[x]) continue;
            dfs2(to[i],to[i]);
        }
    }//以上为树剖预处理
    
    //----------------------------------------------------------------
    int t[N<<2],l_col[N<<2],r_col[N<<2],laz[N<<2];//t存颜色段数量,l_col和r_col分别存左右端点的颜色,laz为懒标记
    inline void pushdown(int &o)//下传laz
    {
        if(laz[o])
        {
            t[o<<1]=t[o<<1|1]=1;
            laz[o<<1]=laz[o<<1|1]=l_col[o<<1]=r_col[o<<1]=l_col[o<<1|1]=r_col[o<<1|1]=laz[o];
            laz[o]=0;
        }
    }
    inline void pushup(int &o)//用儿子节点更新当前节点
    {
        t[o]=t[o<<1]+t[o<<1|1] - (r_col[o<<1]==l_col[o<<1|1] ? 1 : 0);
        l_col[o]=l_col[o<<1]; r_col[o]=r_col[o<<1|1];
    }
    void build(int o,int l,int r)//建树
    {
        if(l==r)
        {
            t[o]=1; l_col[o]=r_col[o]=col[l];
            return;
        }
        int mid=l+r>>1;
        build(o<<1,l,mid); build(o<<1|1,mid+1,r);
        pushup(o);
    }
    void change(int o,int l,int r,int ql,int qr,int Col)//改变颜色
    {
        if(l>=ql&&r<=qr)
        {
            t[o]=1; l_col[o]=r_col[o]=laz[o]=Col;
            return;
        }
        pushdown(o);
        int mid=l+r>>1;
        if(ql<=mid) change(o<<1,l,mid,ql,qr,Col);
        if(qr>mid) change(o<<1|1,mid+1,r,ql,qr,Col);
        pushup(o);
    }
    int Lc=0,Rc=0;//记录最左和最右的颜色
    int query(int o,int l,int r,int ql,int qr)//询问一个区间有多少颜色段
    {
        if(l>=ql&&r<=qr)
        {
            if(l==ql) Lc=l_col[o];//记录左右端点颜色
            if(r==qr) Rc=r_col[o];
            return t[o];
        }
        pushdown(o);
        int mid=l+r>>1,res=0;
        if(ql<=mid) res+=query(o<<1,l,mid,ql,qr);
        if(qr>mid)  res+=query(o<<1|1,mid+1,r,ql,qr);
        if(ql<=mid&&qr>mid) res-=(r_col[o<<1]==l_col[o<<1|1] ? 1 : 0);//注意判断颜色相同
        pushup(o);
        return res;
    }
    //----------------------------------------------------------------
    
    void Color(int x,int y,int &z)//改变两点间的颜色
    {
        while(Top[x]!=Top[y])
        {
            if(dep[Top[x]]<dep[Top[y]]) swap(x,y); 
            change(1,1,n,id[Top[x]],id[x],z);
            x=fa[Top[x]];
        }
        if(dep[x]<dep[y]) swap(x,y);
        change(1,1,n,id[y],id[x],z);
    }
    void Query(int x,int y)//询问两点间的颜色段数量
    {
        int res=0,xc=0,yc=0;//xc,yc存树上两边当前最上面的颜色
        while(Top[x]!=Top[y])
        {
            if(dep[Top[x]]<dep[Top[y]]) swap(x,y),swap(xc,yc);//注意xc,yc也要交换
            res+=query(1,1,n,id[Top[x]],id[x]);
            if( Rc==xc ) res--;//如果取出的区间最右(即最下)边与当前最上面的颜色一样,就要减1
            x=fa[Top[x]];  xc=Lc;
        }
        if(dep[x]<dep[y]) swap(x,y),swap(xc,yc);
        res+=query(1,1,n,id[y],id[x]);
        if(Rc==xc) res--; if(Lc==yc) res--;//注意最后一步要与两边都判一下
        printf("%d
    ",res);
    }
    
    int main()
    {
        int a,b;
        n=read(); m=read();
        for(int i=1;i<=n;i++) c[i]=read();
        for(int i=1;i<n;i++) a=read(),b=read(),add(a,b),add(b,a);
    
        int rt=1;
        dfs1(rt,rt); dfs2(rt,rt);
        build(1,1,n);
    
        char ch[5];
        int x;
        for(int i=1;i<=m;i++)
        {
            scanf("%s",ch); a=read(); b=read();
            if(ch[0]=='C')
            {
                x=read();
                Color(a,b,x);
            }
            else Query(a,b);
        }
        return 0;
    }
  • 相关阅读:
    FILE 创建
    jfreechart折线图 demo
    Win7下Maven的安装与配置
    IntelliJ IDEA 14.x 与 Tomcat 集成,创建并运行Java Web项目
    Java中print、printf、println
    添加SSH密钥到GitHub
    GitHub学习资料
    Windows下Git的安装及配置
    【转】我害怕阅读的人
    安装MongoDB
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9770456.html
Copyright © 2020-2023  润新知