【bzoj2243】 [SDOI2011]染色 树链剖分+线段树

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

Source

第一轮day1


如果是序列的话就是线段树傻X题吧…
改成树上,就剖一下,区间修改+区间查询而已…

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int size=1000010;

int head[size],nxt[size],to[size],tot=0;

void build(int f,int t)
{
    to[++tot]=t;
    nxt[tot]=head[f];
    head[f]=tot;
}

int fa[size],sz[size],son[size],deep[size];
int inseg[size],top[size];

void dfs_1(int u,int f)
{
    deep[u]=deep[f]+1;
    sz[u]=1;
    fa[u]=f;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v==f) continue;
        dfs_1(v,u);
        sz[u]+=sz[v];
        if(!son[u]||sz[v]>sz[son[u]]) son[u]=v;
    }
}

int totp=0;
void dfs_2(int u,int topu)
{
    top[u]=topu;
    inseg[u]=++totp;
    if(!son[u]) return;
    dfs_2(son[u],topu);
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v==fa[u]||v==son[u]) continue;
        dfs_2(v,v);
    }
}


struct segment{
    int l,r;
    int ans,lc,rc,c;
}tree[size];

void update(int p)
{
    tree[p].lc=tree[p<<1].lc;
    tree[p].rc=tree[p<<1|1].rc;
    if(tree[p<<1].rc==tree[p<<1|1].lc)
        tree[p].ans=tree[p<<1].ans+tree[p<<1|1].ans-1;
    else
        tree[p].ans=tree[p<<1].ans+tree[p<<1|1].ans;
}

void build_tree(int p,int l,int r)
{
    tree[p].l=l; tree[p].r=r;
    if(l==r)
    {
        tree[p].ans=tree[p].lc=tree[p].rc=tree[p].c=0;
        return ;
    }
    int mid=(l+r)>>1;
    build_tree(p<<1,l,mid);
    build_tree(p<<1|1,mid+1,r);
    update(p);
}

void spread(int p)
{
    if(tree[p].c)
    {
        tree[p<<1|1].ans=1;
        tree[p<<1|1].lc=tree[p].c;
        tree[p<<1|1].rc=tree[p].c;
        tree[p<<1|1].c=tree[p].c;

        tree[p<<1].ans=1;
        tree[p<<1].lc=tree[p].c;
        tree[p<<1].rc=tree[p].c;
        tree[p<<1].c=tree[p].c;
        tree[p].c=0;
    }
}

void change(int p,int l,int r,int c)
{
    if(l<=tree[p].l&&tree[p].r<=r)
    {
        tree[p].ans=1;
        tree[p].lc=c;   tree[p].rc=c;
        tree[p].c=c;
        return ;
    }
    spread(p);
    int mid=(tree[p].l+tree[p].r)>>1;
    if(l<=mid) change(p<<1,l,r,c);
    if(mid<r) change(p<<1|1,l,r,c);
    update(p);
}

int ask_ans(int p,int l,int r)
{
    if(l<=tree[p].l&&tree[p].r<=r)
    {
        return tree[p].ans;
    }
    spread(p);
    int mid=(tree[p].l+tree[p].r)>>1;
    int ans=0;
    if(l<=mid&&mid<r) 
    {
        if(tree[p<<1].rc==tree[p<<1|1].lc)
            ans+=ask_ans(p<<1,l,mid)+ask_ans(p<<1|1,mid+1,r)-1;
        else
            ans+=ask_ans(p<<1,l,mid)+ask_ans(p<<1|1,mid+1,r);
    }
    else if(l<=mid) ans+=ask_ans(p<<1,l,r);
    else if(mid<r) ans+=ask_ans(p<<1|1,l,r);
    return ans;
}

int ask_color(int p,int x)
{
    if(tree[p].l==tree[p].r)
    {
        return tree[p].lc;
    }
    spread(p);
    int mid=(tree[p].l+tree[p].r)>>1;
    if(x<=mid) return ask_color(p<<1,x);
    else return ask_color(p<<1|1,x);
}

int find_ans(int x,int y)
{
    int fx=top[x],fy=top[y];
    int ans=0;
    while(fx!=fy)
    {
        if(deep[fx]<deep[fy]) swap(x,y),swap(fx,fy);
        ans+=ask_ans(1,inseg[fx],inseg[x]);

        if(ask_color(1,inseg[fx])==ask_color(1,inseg[fa[fx]]))
            ans--;      
        x=fa[fx],fx=top[x];
    }
    if(deep[x]>deep[y]) swap(x,y);
    ans+=ask_ans(1,inseg[x],inseg[y]);
    return ans;
}

void find_change(int x,int y,int c)
{
    int fx=top[x],fy=top[y];
    while(fx!=fy)
    {
        if(deep[fx]<deep[fy]) swap(x,y),swap(fx,fy);
        change(1,inseg[fx],inseg[x],c); 
        x=fa[fx],fx=top[x];
    }
    if(deep[x]>deep[y]) swap(x,y);
    change(1,inseg[x],inseg[y],c);
}

int data[size];
int main()
{
    freopen("2243.in","r",stdin);
    freopen("2243.out","w",stdout);
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&data[i]);
    }
    for(int i=1;i<=n-1;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        build(a,b); build(b,a);
    }

    dfs_1(1,0); dfs_2(1,1);

    build_tree(1,1,n);

    for(int i=1;i<=n;i++)
    {
        change(1,inseg[i],inseg[i],data[i]);
    }

    while(m--)
    {
        char s[5];
        scanf("%s",s);
        switch(s[0])
        {
            case 'C':
                int l,r,c;
                scanf("%d%d%d",&l,&r,&c);
                if(inseg[l]>inseg[r]) swap(l,r);
                find_change(l,r,c);
                break;
            case 'Q':
                int a,b;
                scanf("%d%d",&a,&b);
                printf("%d\n",find_ans(a,b));               
                break;
        }
    }

    return 0;
}
/*
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
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值