BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 1886  Solved: 752
[Submit][Status]

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,表示xy之间有一条无向边。

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

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

对于线段树区间操作与顺序有关的题目,如这道题,要特别注意区间提取与合并的顺序。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define lch (now<<1)
#define rch (now<<1^1)
#define MAXN 110000
#define MAXT 400010
#define MAXE 200010
int n,m;
int col1[MAXN];
int col2[MAXN];
struct node
{
        int lc,rc;
        int l,r;
        int tot;
        int flag;
}tree[MAXT];
void update(int now)
{
        if (tree[now].l==tree[now].r)tree[now].tot=1;
        else
        {
                tree[now].tot=tree[lch].tot+tree[rch].tot-(tree[lch].rc==tree[rch].lc);
                tree[now].lc=tree[lch].lc;
                tree[now].rc=tree[rch].rc;
        }
}
void down(int now)
{
        if (tree[now].l==tree[now].r)
        {
                if (tree[now].flag)
                        tree[now].flag=false;
                return ;
        }
        if (tree[now].flag)
        {
                tree[lch].flag=tree[rch].flag=tree[now].flag;
                tree[lch].tot=1;
                tree[lch].lc=tree[lch].rc=tree[now].flag;
                tree[rch].tot=1;
                tree[rch].lc=tree[rch].rc=tree[now].flag;
                tree[now].flag=0;
        }
}
void build_tree(int now,int l,int r)
{
        tree[now].l=l;
        tree[now].r=r;
        tree[now].lc=col2[l];
        tree[now].rc=col2[r];
        tree[now].flag=0;
        if (l==r)
        {
                tree[now].tot=1;
                return ;
        }
        int mid=(l+r)/2;
        build_tree(lch,l,mid);
        build_tree(rch,mid+1,r);
        update(now);
}
node comb_seg(node n1,node n2)
{
        node ret;
        ret.lc=n1.lc;
        ret.rc=n2.rc;
        ret.tot=n1.tot+n2.tot-(n1.rc==n2.lc);
        return ret;
}
node turn (node now)
{
        swap(now.lc,now.rc);
        return now;
}
node get_seg(int now,int l,int r)
{
        int i,j,k;
        node ret;
        bool flip=false;
        if (l>r)swap(l,r),flip=true;
        down(now);
        if (tree[now].l==l&&tree[now].r==r)
        {
                ret=tree[now];
                if (flip)turn(ret);
                return ret;
        }
        int mid=(tree[now].l+tree[now].r)/2;
        if (r<=mid)
        {
                if (flip)return turn(get_seg(lch,l,r));
                else return get_seg(lch,l,r);
        }
        if (mid<l)
        {
                if (flip)return turn(get_seg(rch,l,r));
                else return get_seg(rch,l,r);
        }
        ret=comb_seg(get_seg(lch,l,mid),get_seg(rch,mid+1,r));
        if (flip)return turn(ret);
        else return ret;
}
void set_val(int now,int l,int r,int v)
{
        if (l>r)swap(l,r);
        if (tree[now].l==l&&tree[now].r==r)
        {
                tree[now].tot=1;
                tree[now].lc=tree[now].rc=v;
                tree[now].flag=v;
                return ;
        }
        down(now);
        int mid=(tree[now].l+tree[now].r)/2;
        if (r<=mid)
        {
                set_val(lch,l,r,v);
                update(now);
                return ;
        }
        if (mid<l)
        {
                set_val(rch,l,r,v);
                update(now);
                return ;
        }
        set_val(lch,l,mid,v);
        set_val(rch,mid+1,r,v);
        update(now);
}
struct Edge
{
        int np;
        Edge *next;
}E[MAXE],*V[MAXN];
int tope=-1,root;
void addedge(int x,int y)
{
        E[++tope].np=y;
        E[tope].next=V[x];
        V[x]=&E[tope];
}

int siz[MAXN];
int depth[MAXN];
int fa[MAXN];
int son[MAXN];
int dfn[MAXN],cnt=0;
int ptr[MAXN];
int top[MAXN];
int dfs1(int now,int d)
{
        Edge *ne;
        int t,mx=0;
        depth[now]=d;
        siz[now]=1;
        son[now]=0;
        for (ne=V[now];ne;ne=ne->next)
        {
                if(ne->np==fa[now])continue;
                fa[ne->np]=now;
                t=dfs1(ne->np,d+1);
                if (t>mx)
                {
                        mx=t;
                        son[now]=ne->np;
                }
                siz[now]+=t;
        }
        return siz[now];
}

void dfs2(int now,int tp)
{
        Edge *ne;
        dfn[now]=++cnt;
        ptr[cnt]=now;
        top[now]=tp;
        if (son[now])
                dfs2(son[now],tp);
        for (ne=V[now];ne;ne=ne->next)
        {
                if (ne->np==fa[now]||ne->np==son[now])continue;
                dfs2(ne->np,ne->np);
        }
}
int jump[MAXN][21];
void init_lca()
{
        int i,j;
        for (i=1;i<=n;i++)
        {
                jump[i][0]=fa[i];
        }
        for (j=1;j<20;j++)
        {
                for (i=1;i<=n;i++)
                {
                        jump[i][j]=jump[jump[i][j-1]][j-1];
                }
        }
}
int swim(int x,int d)
{
        int i=0,ret=x;
        while (d)
        {
                if (d&1)
                {
                        ret=jump[ret][i];
                }
                i++;
                d>>=1;
        }
        return ret;
}
int get_lca(int x,int y)
{
        if (depth[x]>depth[y])
                x=swim(x,depth[x]-depth[y]);
        else
                y=swim(y,depth[y]-depth[x]);
        if (x==y)return x;
        int i;
        for (i=19;i>=0;i--)
        {
                if (jump[x][i]!=jump[y][i])
                {
                        x=jump[x][i];
                        y=jump[y][i];
                }
        }
        return fa[x];
}
int query(int x,int y)
{
        int lca;
        lca=get_lca(x,y);
        node n1,n2;
        n1.tot=0;
        n1.rc=0;
        n1.lc=0;
        n1.l=dfn[x];
        n1.r=dfn[x];
        n2=n1;
        while (true)
        {
                if (top[x]==top[lca])
                {
                        n1=comb_seg(n1,get_seg(1,dfn[x],dfn[lca]));
                        break;
                }
                n1=comb_seg(n1,get_seg(1,dfn[x],dfn[top[x]]));
                x=fa[top[x]];
        }
        if (lca==y)return n1.tot;
        int d=depth[y]-depth[lca]-1;;
        int t=swim(y,d);
        while (true)
        {
                if (top[y]==top[t])
                {
                        n2=comb_seg(get_seg(1,dfn[t],dfn[y]),n2);
                        break;
                }
                n2=comb_seg(get_seg(1,dfn[top[y]],dfn[y]),n2);
                y=fa[top[y]];
        }
        n1=comb_seg(n1,n2);
        return n1.tot;

}
void paint_color(int x,int y,int z)
{
        int lca;
        lca=get_lca(x,y);
        while (true)
        {
                if (top[x]==top[lca])
                {
                        set_val(1,dfn[x],dfn[lca],z);
                        break;
                }
                set_val(1,dfn[x],dfn[top[x]],z);
                x=fa[top[x]];
        }
        if (y==lca)return ;
        int d=depth[y]-depth[lca]-1;;
        int t=swim(y,d);
        while (true)
        {
                if (top[y]==top[t])
                {
                        set_val(1,dfn[y],dfn[t],z);
                        break;
                }
                set_val(1,dfn[y],dfn[top[y]],z);
                y=fa[top[y]];
        }
}
int main()
{
        //freopen("input.txt","r",stdin);
        int i,j,k,x,y,z;
        scanf("%d%d",&n,&m);
        for (i=0;i<n;i++)
        {
                scanf("%d",col1+i+1);
        }
        for (i=1;i<n;i++)
        {
                scanf("%d%d",&x,&y);
                addedge(x,y);
                addedge(y,x);
        }
        root=1;
        fa[root]=root;
        dfs1(root,0);
        dfs2(root,root);
        init_lca();
        for (i=1;i<=n;i++)
                col2[dfn[i]]=col1[i];
        build_tree(1,1,cnt);
        scanf("\n");
        char opt;
        while (m--)
        {
                scanf("%c",&opt);
                if (opt==‘Q‘)
                {
                        scanf("%d%d\n",&x,&y);
                        printf("%d\n",query(x,y));
                }else
                {
                        scanf("%d%d%d\n",&x,&y,&z);
                        paint_color(x,y,z);
                }
        }
}

BZOJ 2243: [SDOI2011]染色 树链剖分,布布扣,bubuko.com

时间: 2024-10-13 00:59:32

BZOJ 2243: [SDOI2011]染色 树链剖分的相关文章

BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=2243 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写

BZOJ 2243: [SDOI2011]染色 树链剖分+线段树区间合并

2243: [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之间有一条无向边.

[bzoj 2243]: [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

bzoj-2243 2243: [SDOI2011]染色(树链剖分)

题目链接: 2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6267  Solved: 2291 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. Input 第一行包含

2243: [SDOI2011]染色(树链剖分+线段树)

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 8400  Solved: 3150[Submit][Status][Discuss] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完

2243: [SDOI2011]染色树链剖分

对于线段树的操作,维护左端值,维护右端值,维护种类数,更新的时候,如果左儿子的右端==右儿子的左端,种类数减一,剩下的就是细节了. #include<iostream> #include<cstdio> #include<cstring> #include<map> #include<vector> #include<stdlib.h> using namespace std; #define lson l,mid,rt<<

[BZOJ2243]SDOI2011染色|树链剖分|LCT

裸题嘛.. 先考虑一条线段上如何查询颜色段数,只要对每个线段树节点多维护一个左颜色和右颜色,然后合并的时候sum[x]=sum[lc]+sum[rc]-(左儿子的右颜色==右儿子的左颜色)..实在太久没写树剖结果码+调试花了两节多晚自习,,各种傻逼错误,什么反向边忘加,标记忘记下传...还有就是更新答案的时候,关键的一点是要保证当前的两点(也就是a,b)是没有被更新到的,否则很难搞.. 表示LCT要更好写..不过在BZOJ上我的树链剖分6000+MS,LCT要13000+MS.. 树链剖分: #

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

[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

luogu题解P2486[SDOI2011]染色--树链剖分+trick

题目链接 https://www.luogu.org/problemnew/show/P2486 分析 看上去又是一道强行把序列上问题搬运到树上的裸题,然而分析之后发现并不然... 首先我们考虑如何在序列上维护信息:从最简单的想起,如果两个相邻的元素合并,显然是这两个元素所含颜色段个数(其实就是1)加起来,如果两个元素颜色相同就减1;那么两个分别含有两个元素的相邻区间合并,还是把这两个区间所含颜色段个数加起来,如果左区间最右边的颜色等于右区间最左边的颜色就减去1. 如此我们已经得到线段树维护信息