bzoj 2243 树链剖分 染色

2243: [SDOI2011]染色

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 3205  Solved: 1238
[ Submit][ Status][ Discuss]

Description

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

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

2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“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

第一轮day1

#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
//siz[v]表示以v为根的子树的节点数
//dep[v]表示v的深度(根深度为1)
//top[v]表示v所在的链的顶端节点
//fa[v]表示v的父亲
//son[v]表示与v在同一重链上的v的儿子节点(姑且称为重儿子)
//tid[v]表示v与其父亲节点的连边(姑且称为v的父边)在线段树中的位置
const int maxn=500010;
int n;
int siz[maxn],dep[maxn],top[maxn],fa[maxn],son[maxn];
int tid[maxn];
int tim;
int col[maxn];
int rank[maxn];
struct node
{
    int v;
    int next;
    int w;
}e[maxn*2];
int pre[maxn],cnt;
void init()
{
    cnt=0;
    tim=0;
    memset(pre,-1,sizeof(pre));
    memset(son,-1,sizeof(son));
}
void add(int u,int v)
{
    e[cnt].v=v;
    e[cnt].next=pre[u];
    pre[u]=cnt++;
}
//树链剖分部分
void dfs1(int u,int father,int d)//求出dep,fa,siz,son
{
    dep[u]=d;
    fa[u]=father;
    siz[u]=1;
    for(int i=pre[u];i!=-1;i=e[i].next)
    {
        int v=e[i].v;
        if(v==father) continue;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(son[u]==-1||siz[v]>siz[son[u]])
            son[u]=v;
    }
}
void dfs2(int u,int tp)
{
     top[u]=tp;
     tid[u]=++tim;
     rank[tim]=u;
     if(son[u]==-1) return ;
     dfs2(son[u],tp);
     for(int i=pre[u];i!=-1;i=e[i].next)
     {
        int v=e[i].v;
        if(v!=son[u]&&v!=fa[u])
            dfs2(v,v);
     }
}
//线段树部分
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int inf=1e9;
int num[4*maxn];
int lc[4*maxn];
int rc[4*maxn];
int lazy[4*maxn];
void pushup(int rt)
{
    lc[rt]=lc[rt<<1];
    rc[rt]=rc[rt<<1|1];
    int ans=num[rt<<1]+num[rt<<1|1];
    if(rc[rt<<1]==lc[rt<<1|1])
        ans--;
    num[rt]=ans;
}
void pushdown(int rt)
{
    if(lazy[rt])
    {
        lazy[rt<<1]=lazy[rt<<1|1]=1;
        num[rt<<1]=num[rt<<1|1]=1;
        lc[rt<<1]=rc[rt<<1]=lc[rt];
        lc[rt<<1|1]=rc[rt<<1|1]=lc[rt];
        lazy[rt]=0;
    }
}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        num[rt]=1;
        lc[rt]=col[rank[l]];
        rc[rt]=col[rank[r]];
        return ;
    }
   int mid=(l+r)>>1;
   build(lson);
   build(rson);
   pushup(rt);
}
void update(int l,int r,int rt,int L,int R,int val)
{
    if(L<=l&&r<=R)
    {
        lazy[rt]=1;
        num[rt]=1;
        lc[rt]=rc[rt]=val;
        return ;
    }
    pushdown(rt);
    int mid=(l+r)>>1;
    if(R<=mid)
        update(lson,L,R,val);
    else if(L>mid)
        update(rson,L,R,val);
    else
       {
           update(lson,L,R,val);update(rson,L,R,val);
       }
    pushup(rt);
}
int LC,RC;
int query(int l,int r,int rt,int L,int R)
{
    if(l==L)
        LC=lc[rt];
    if(r==R)
        RC=rc[rt];
    if(L<=l&&R>=r)
        return num[rt];
    pushdown(rt);
    int mid=(l+r)>>1;
    if(R<=mid) return query(lson,L,R);
    else if(L>mid)
               return query(rson,L,R);
    else
    {
         int ans=query(lson,L,R)+query(rson,L,R);
         if(rc[rt<<1]==lc[rt<<1|1])
            ans--;
         return ans;
    }

}
int solve(int x,int y,int id,int c)
{
    int ans=0;
    if(id==1)
    {
       while(top[x]!=top[y])
       {
           if(dep[top[x]]<dep[top[y]]) swap(x,y);//先搜top深度大的链
           update(1,n,1,tid[top[x]],tid[x],c);
           x=fa[top[x]];
       }
       if(dep[x]>dep[y]) swap(x,y);
       update(1,n,1,tid[x],tid[y],c);
    }
    else
    {
        int ans1=-1,ans2=-1;//记录上次链的左端的颜色 ans1与ans2分别指的两边上次链的左端颜色(接近LCA的那端的颜色)
        while(top[x]!=top[y])
        {
         if(dep[top[x]]<dep[top[y]]) {swap(x,y);swap(ans1,ans2);}//ans1始终跟着x跑
           ans+=query(1,n,1,tid[top[x]],tid[x]);
           if(RC==ans1)
            ans--;
            ans1=LC;
           x=fa[top[x]];
        }
        if(dep[x]>dep[y]){ swap(x,y);swap(ans1,ans2);}//ans1始终跟着x跑
       ans+=query(1,n,1,tid[x],tid[y]);
       //注意:因为这里弄反了WA了无数次
       if(RC==ans2)
        ans--;
       if(LC==ans1)
        ans--;
       return ans;
    }
}
int m;
int main()
{
    char op[15];
   //freopen("in.txt","r",stdin);
    while(~scanf("%d%d",&n,&m))
    {
        init();
        for(int i=1;i<=n;i++)
            scanf("%d",&col[i]);
        for(int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        dfs1(1,1,1);
        dfs2(1,1);
        build(1,n,1);
          while(m--)
          {
              char str[20];
              scanf("%s",str);
              int u,v,c;
              if(str[0]=='C')
              {
                  scanf("%d%d%d",&u,&v,&c);
                  solve(u,v,1,c);
              }
              else
              {
                  int u,v;
                  scanf("%d%d",&u,&v);
                  printf("%d\n",solve(u,v,2,0));
              }
          }
    }





}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值