【SDOI2011】bzoj2243 染色

177 篇文章 1 订阅
148 篇文章 0 订阅

Description

给定一棵有n个节点的无根树和m个操作,操作有2类:

1、将节点a到节点b路径上所有点都染成颜色c;

2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。

请你写一个程序依次完成这m个操作。

Input

第一行包含2个整数n和m,分别表示节点数和操作数;

第二行包含n个正整数表示n个节点的初始颜色

下面 行每行包含两个整数x和y,表示x和y之间有一条无向边。

下面 行每行描述一个操作:

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

“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。

Output

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

树剖之后对线段树的每个节点记录颜色数、左端点颜色和右端点颜色,方便合并。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int fa[100010],son[100010],dep[100010],top[100010],pos[100010],size[100010],
num[1000010],tag[1000010],lc[1000010],rc[1000010],
fir[100010],ne[200010],to[200010],clr[100010],
m,n,tot;
void add(int num,int u,int v)
{
    ne[num]=fir[u];
    fir[u]=num;
    to[num]=v;
}
void init()
{
    int i,x,y;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++)
      scanf("%d",&clr[i]);
    for (i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(i*2,x,y);
        add(i*2+1,y,x);
    }
}
void down(int p)
{
    if (tag[p]>=0)
    {
        tag[p*2]=tag[p*2+1]=lc[p]=rc[p]=tag[p];
        num[p]=1;
        tag[p]=-1;
    }
}
void up(int p)
{
    down(p);
    down(p*2);
    down(p*2+1);
    lc[p]=lc[p*2];
    rc[p]=rc[p*2+1];
    num[p]=num[p*2]+num[p*2+1]-(bool)(rc[p*2]==lc[p*2+1]);
}
void modi(int p,int L,int R,int l,int r,int x)
{
    down(p);
    if (L==l&&R==r)
    {
        tag[p]=x;
        return;
    }
    int mid=(L+R)/2;
    if (r<=mid) modi(p*2,L,mid,l,r,x);
    else
    {
        if (l>=mid+1) modi(p*2+1,mid+1,R,l,r,x);
        else
        {
            modi(p*2,L,mid,l,mid,x);
            modi(p*2+1,mid+1,R,mid+1,r,x);
        }
    }
    up(p);
}
int qry(int p,int L,int R,int l,int r,int &ll,int &rr)
{
    down(p);
    if (L==l&&R==r)
    {
        ll=lc[p];
        rr=rc[p];
        return num[p];
    }
    int mid=(L+R)/2;
    if (r<=mid) return qry(p*2,L,mid,l,r,ll,rr);
    if (l>=mid+1) return qry(p*2+1,mid+1,R,l,r,ll,rr);
    int a1,l1,r1,a2,l2,r2;
    a1=qry(p*2,L,mid,l,mid,l1,r1);
    a2=qry(p*2+1,mid+1,R,mid+1,r,l2,r2);
    ll=l1;
    rr=r2;
    return a1+a2-(bool)(r1==l2);
}
void dfs1(int u,int f)
{
    int i,v;
    size[u]=1;
    for (i=fir[u];i;i=ne[i])
      if ((v=to[i])!=f)
      {
        dep[v]=dep[u]+1;
        fa[v]=u;
        dfs1(v,u);
        size[u]+=size[v];
        if (son[u]==0||size[v]>size[son[u]])
          son[u]=v;
      }
}
void dfs2(int u)
{
    int i,v;
    pos[u]=++tot;
    if (!son[u]) return;
    top[son[u]]=top[u];
    dfs2(son[u]);
    for (i=fir[u];i;i=ne[i])
      if ((v=to[i])!=son[u]&&v!=fa[u])
      {
        top[v]=v;
        dfs2(v);
      }
}
void pre()
{
    int i;
    dep[1]=1;
    dfs1(1,-1);
    top[1]=1;
    dfs2(1);
    memset(tag,-1,sizeof(tag));
    for (i=1;i<=n;i++)
      modi(1,1,tot,pos[i],pos[i],clr[i]);
}
void modify(int u,int v,int x)
{
    int f1,f2;
    while ((f1=top[u])!=(f2=top[v]))
    {
        if (dep[f1]<dep[f2])
        {
            swap(f1,f2);
            swap(u,v);
        }
        modi(1,1,tot,pos[f1],pos[u],x);
        u=fa[f1];
    }
    if (dep[u]<dep[v])
      swap(u,v);
    modi(1,1,tot,pos[v],pos[u],x);
}
int query(int u,int v)
{
    int l1=-1,l2=-1,ret=0,f1,f2,tl,tr,temp;
    while ((f1=top[u])!=(f2=top[v]))
      if (dep[f1]<dep[f2])
      {
        temp=qry(1,1,tot,pos[f2],pos[v],tl,tr);
        ret+=temp-(bool)(l2==tr);
        l2=tl;
        v=fa[f2];
      }
      else
      {
        temp=qry(1,1,tot,pos[f1],pos[u],tl,tr);
        ret+=temp-(bool)(l1==tr);
        l1=tl;
        u=fa[f1];
      }
    if (dep[u]<dep[v])
    {
        temp=qry(1,1,tot,pos[u],pos[v],tl,tr);
        ret+=temp-(bool)(l1==tl)-(bool)(l2==tr);
    }
    else
    {
        temp=qry(1,1,tot,pos[v],pos[u],tl,tr);
        ret+=temp-(bool)(l2==tl)-(bool)(l1==tr);
    }
    return ret;
}
int main()
{
    char s[5];
    int x,y,z;
    init();
    pre();
    while (m--)
    {
        scanf("%s",s);
        if (s[0]=='C')
        {
            scanf("%d%d%d",&x,&y,&z);
            modify(x,y,z);
        }
        else
        {
            scanf("%d%d",&x,&y);
            printf("%d\n",query(x,y));
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值