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\n",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;
}

 

posted @ 2018-10-11 08:34  LLTYYC  阅读(229)  评论(0编辑  收藏  举报