BZOJ 2243 染色 线段树+树链剖分

2243: [SDOI2011]染色

Time Limit: 20 Sec Memory Limit: 512 MB
Submit: 7964 Solved: 2987
[Submit][Status][Discuss]

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

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

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]之间。

题解:树链剖分+线段树,注意每次查询时查询的是断断续续的区间,所以每次要继续下上一次区间的信息,如果两次区间的始末位置颜色相同的话,合并之后的颜色数就要-1。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

const int N = 200010;

int n,m;
int a[N],co[N];

struct node{
    int pre,v;
}edge[N];

int num=0,head[N];
void addedge(int from,int to){
    num++;
    edge[num].pre=head[from];
    edge[num].v=to;
    head[from]=num;
}

int fa[N],dep[N];
int siz[N],son[N];

void dfs1(int u,int f,int d){
    siz[u]=1,fa[u]=f,dep[u]=d;
    for(int i=head[u];i;i=edge[i].pre){
        int v=edge[i].v;
        if(v==f) continue;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(son[u]==-1||siz[v]>siz[son[u]]) son[u]=v;
    }
}

int indx=0;
int in[N],out[N],top[N];
void dfs2(int u,int tp){
    in[u]=out[u]=++indx;
    co[indx]=a[u];
    top[u]=tp;
    if(son[u]==-1) return ;
    dfs2(son[u],tp);
    for(int i=head[u];i;i=edge[i].pre){
        int v=edge[i].v;
        if(v==son[u]||v==fa[u]) continue;
        dfs2(v,v);
    }
    out[u]=indx;
}

struct Node{
    int ans;
    int lx,rx;
    int l,r;
    int flag;
}t[N<<4];

void update(int root){
    if(t[root<<1].rx==t[root<<1|1].lx)
        t[root].ans=t[root<<1].ans+t[root<<1|1].ans-1;
    else t[root].ans=t[root<<1].ans+t[root<<1|1].ans;
    t[root].lx=t[root<<1].lx,t[root].rx=t[root<<1|1].rx;
}

void build(int root,int l,int r){
    t[root].l=l,t[root].r=r,t[root].flag=0;
    if(l==r){
        t[root].ans=1;
        t[root].lx=t[root].rx=co[l];
        return ;
    }
    int mid=l+r>>1;
    build(root<<1,l,mid);
    build(root<<1|1,mid+1,r);
    update(root);
}

void pushdown(int root){
    int flag=t[root].flag,l=t[root].l,r=t[root].r;
    if(flag){
        t[root<<1].ans=t[root<<1|1].ans=1;
        t[root<<1].lx=t[root<<1].rx=flag;
        t[root<<1|1].lx=t[root<<1|1].rx=flag;
        t[root<<1].flag=t[root<<1|1].flag=flag;
        t[root].flag=0;
    }
}

void modify(int root,int pos,int val,int delta){
    int l=t[root].l,r=t[root].r;
    if(pos<=l&&val>=r){
        t[root].ans=1;
        t[root].lx=t[root].rx=delta;
        t[root].flag=delta;
        return ;
    }
    int mid=l+r>>1;
    pushdown(root);
    if(pos<=mid) modify(root<<1,pos,val,delta);
    if(val>mid) modify(root<<1|1,pos,val,delta);
    update(root);
}

void modify(int u,int v,int delta){
    int f1=top[u],f2=top[v];
    while(f1!=f2){
        if(dep[f1]<dep[f2]) swap(f1,f2),swap(u,v);
        modify(1,in[f1],in[u],delta);
        u=fa[f1],f1=top[u];
    }
    if(dep[u]>dep[v]) swap(u,v);
    modify(1,in[u],in[v],delta);
}

int lc,rc;
int query(int root,int pos,int val,int L,int R){
    int l=t[root].l,r=t[root].r;
    if(l==L) lc=t[root].lx;
    if(r==R) rc=t[root].rx;
    if(pos==l&&val==r) return t[root].ans;
    int mid=l+r>>1;
    pushdown(root);
    if(val<=mid) return query(root<<1,pos,val,L,R);
    else if(pos>mid) return query(root<<1|1,pos,val,L,R);
    else{
        int a=query(root<<1,pos,mid,L,R);
        int b=query(root<<1|1,mid+1,val,L,R);
        if(t[root<<1|1].lx==t[root<<1].rx) return a+b-1;
        else return a+b;
    }
}

int query(int u,int v){
    int f1=top[u],f2=top[v];
    int sum=0;
    int co1=-1,co2=-1;
    while(f1!=f2){
        if(dep[f1]<dep[f2]) swap(f1,f2),swap(u,v),swap(co1,co2);
        sum+=query(1,in[f1],in[u],in[f1],in[u]);
        if(rc==co1) sum--;
        co1=lc;
        u=fa[f1];
        f1=top[u];
    }
    if(dep[u]<dep[v]) swap(u,v),swap(co1,co2);
    sum+=query(1,in[v],in[u],in[v],in[u]);
    if(rc==co1) sum--;if(lc==co2) sum--;
    return sum;
}

#define ms(x,y) memset(x,y,sizeof(x))
int main(){
    ms(son,-1);
    scanf("%d%d",&n,&m);
    for(register int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(register int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        addedge(u,v);addedge(v,u);
    }
    dfs1(1,0,0);dfs2(1,1);
    build(1,1,n);
    while(m--){
        char s[10];
        int u,v,delta;
        scanf("%s",s);
        if(s[0]=='C'){
            scanf("%d%d%d",&u,&v,&delta);
            modify(u,v,delta);
        }
        else{
           scanf("%d%d",&u,&v);
           printf("%d\n",query(u,v));
        }
    }
    return 0;
}

7月60篇博客达成。这个月真是勤勉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值