[SDOI2011] [BZOJ2243] 染色 - 树链剖分

2243: [SDOI2011]染色

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 5001   Solved: 1860
[ Submit][ Status][ Discuss]

Description

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

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

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

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

Input

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

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

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

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

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

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

Output

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

Sample Input

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

Sample Output

3

1

2

HINT

数N<=10^5,操作数M<=10^5,所有的颜色C为整数且在[0, 10^9]之间。

Source


    首先对树轻重链剖分,维护线段树一段的颜色,最左边和最右边的颜色即可。

#include "iostream"
#include "stdio.h"
using namespace std;
inline void read(int &v){
  char ch=getchar();v=0;
  while(ch>'9'||ch<'0')ch=getchar();
  do{v=v*10+ch-'0',ch=getchar();}
  while(ch<='9'&&ch>='0');
}
const int N=100005,oo=0x7fffffff,G=17;
int n,m,color[N],push[N],size[N],deep[N];
int head[N],next[2*N],route[2*N],fa[N][G+1];
int pos[N],belong[N],tmp;
inline void add_arc(int x,int y,int i){
  next[i]=head[x];
  route[i]=y;
  head[x]=i;
}//O(1)
inline int lca(int u,int v){
  if(deep[u]<deep[v]) swap(u,v);
  int len=deep[u]-deep[v],i;
  for (i=G;i>=0;i--) 
   if(len&(1<<i))
    u=fa[u][i];
  for (i=G;i>=0;i--) 
   if(fa[u][i]!=fa[v][i]) 
    u=fa[u][i],v=fa[v][i];
  if(u==v) return u;
  return fa[u][0];
}//O(log n)
int dfs1(int v){int k;size[v]=1;
  for (k=1;(1<<k)<=deep[v];k++){
    fa[v][k]=fa[fa[v][k-1]][k-1];
  }
  for (k=head[v];k;k=next[k]){
    if(deep[route[k]]>deep[v])
    {deep[route[k]]=deep[v]+1;
     fa[route[k]][0]=v;dfs1(route[k]);
     size[v]+=size[route[k]];}
  }
  return size[v];
}//O(n)
void dfs2(int v,int bel){
  int k,u=0; belong[v]=bel;
  pos[v]=++tmp; push[tmp]=color[v];
  for (k=head[v];k;k=next[k])
    if(size[u]<size[route[k]]&&deep[route[k]]>deep[v])
     u=route[k];
  if(!u)return;
  dfs2(u,bel);
  for (k=head[v];k;k=next[k])
    if(route[k]!=u&&deep[route[k]]>deep[v])
     dfs2(route[k],route[k]);
}//O(n)
void build(){int i,u,v;
  for (i=1;i<n;i++){
    read(u),read(v);
    add_arc(u,v,2*i-1);
    add_arc(v,u,2*i);
  }deep[1]=0;
  dfs1(1),dfs2(1,1);
}//O(n)
struct SegMent{
  int l,r,color;
  int lco,rco;
  int ans;
} seg[N<<2];
inline void Build(int l,int r,int v){int mid=(l+r)>>1;
  seg[v].l=l,seg[v].r=r;seg[v].color=0;
  if(l==r){seg[v].color=seg[v].lco=seg[v].rco=push[l]+1;
   seg[v].ans=1; return ;}Build(l,mid,v<<1),Build(mid+1,r,v<<1|1);
  seg[v].lco=seg[v<<1].lco,seg[v].rco=seg[v<<1|1].rco;
  seg[v].ans=seg[v<<1].ans+seg[v<<1|1].ans-(seg[v<<1].rco==seg[v<<1|1].lco);
}//O(n)
inline void paint(int l,int r,int c,int v){
  int mid=(seg[v].l+seg[v].r)>>1;
  if(seg[v].l==l&&seg[v].r==r)
  { seg[v].color=seg[v].lco=seg[v].rco=c;
   seg[v].ans=1; return ;}
  if(seg[v].color==c) return;
  if(seg[v].color)
  { seg[v<<1].color=seg[v<<1|1].color=seg[v<<1].lco=
    seg[v<<1].rco=seg[v<<1|1].lco=seg[v<<1|1].rco=seg[v].color;
    seg[v<<1].ans=seg[v<<1|1].ans=1;
    seg[v].color=0;
   }
  if(r<=mid) paint(l,r,c,v<<1);
  else if(l>mid) paint(l,r,c,v<<1|1);
  else paint(l,mid,c,v<<1),paint(mid+1,r,c,v<<1|1);
  seg[v].lco=seg[v<<1].lco,seg[v].rco=seg[v<<1|1].rco;
  seg[v].ans=seg[v<<1].ans+seg[v<<1|1].ans-(seg[v<<1].rco==seg[v<<1|1].lco);
}//O(log n)
inline int count(int v,int l,int r,int last,int &ans){
  int mid=(seg[v].l+seg[v].r)>>1;
  if(seg[v].l==l&&seg[v].r==r)
  { ans+=seg[v].ans-(last==seg[v].rco); return seg[v].lco; }
  if(seg[v].color)
  { ans+=last!=seg[v].rco; return seg[v].color;
   }
  if(r<=mid) return count(v<<1,l,r,last,ans);
  else if(l>mid) return count(v<<1|1,l,r,last,ans);
  return count(v<<1,l,mid,count(v<<1|1,mid+1,r,last,ans),ans);
}//O(log n)
inline void Color(int u,int v,int co){
  int t=lca(u,v);swap(v,t);
  while (belong[u]!=belong[v]){
    paint(pos[belong[u]],pos[u],co,1);
    u=fa[belong[u]][0];
  }
  paint(pos[v],pos[u],co,1);swap(u,t);
  while (belong[u]!=belong[v]){
    paint(pos[belong[u]],pos[u],co,1);
    u=fa[belong[u]][0];
  }
  paint(pos[v],pos[u],co,1);
}//O(log n^2)
inline int Query(int u,int v){
  int t=lca(u,v);swap(v,t);
  int ans=0,last=-1;
  while (belong[u]!=belong[v]){
    last=count(1,pos[belong[u]],pos[u],last,ans);
    u=fa[belong[u]][0];
  }
  count(1,pos[v],pos[u],last,ans);swap(u,t);last=-1;
  while (belong[u]!=belong[v]){
    last=count(1,pos[belong[u]],pos[u],last,ans);
    u=fa[belong[u]][0];
  }
  last=count(1,pos[v],pos[u],last,ans);
  return ans-1;
}//O(log n^2)
void work(){
  for (int i=0;i<m;i++){
    char ch=getchar();int u,v,c;
    while(ch!='Q'&&ch!='C')ch=getchar();
    read(u),read(v);
    if(ch=='C') read(c),Color(u,v,c+1);
    else printf("%d\n",Query(u,v));
  }
}//O(m log n^2 + n)
int main(){
//    freopen("paint.in","r",stdin);
//    freopen("paint.out","w",stdout);
    read(n),read(m);
    for(int i=1;i<=n;i++)
     read(color[i]),deep[i]=oo;
    build(),Build(1,n,1),work();
    return 0;
}//O(m log n^2 + n)

标复杂度是因为我一开始写TLE了……到最后才发现在LCA那出了一点小问题……


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值