[SDOI2011]染色(树链剖分)

[SDOI2011]染色(luogu)

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)路径上的颜色段数量。

ouput

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

Solution

树链剖分处理色块(注意相邻两段的首尾颜色相同的情况)

主要是细节,详见代码

Code

#include <cstdio>
#include <cstdlib>
#include <vector>
using namespace std;
const int N=1e5+10;
struct node
{
    int l,r,lc,rc,cl,cr,sum;
    bool flag;
}f[N*2];
int fa[N],deep[N],son[N],d[N],dfn[N],si[N],top[N],rt,
tot,cnt,n,m,u,v,c,re[N],L,R,a,b;
char s[5];
vector <int> link[N];
void dfs1(int u,int f)
{
    fa[u]=f;
    deep[u]=deep[f]+1;
    si[u]=1;
    int size=link[u].size();
    for(int i=0;i<size;i++)
    {
        int v=link[u][i];
        if(v!=f)
        {
            dfs1(v,u);
            si[u]+=si[v];
            if(son[u]==0 || si[son[u]]<si[v]) son[u]=v;
        }
    }
}
void dfs2(int u,int f)
{
    dfn[u]=++cnt,re[cnt]=u;
    if(son[u]!=0) top[son[u]]=top[u],dfs2(son[u],u);
    int size=link[u].size();
    for(int i=0;i<size;i++)
    {
        int v=link[u][i];
        if(v!=f && v!=son[u])
            top[v]=v,dfs2(v,u);
    }
}
void push_up(int g)
{
    int lc=f[g].lc,rc=f[g].rc;
    f[g].cl=f[lc].cl,f[g].cr=f[rc].cr;
    f[g].sum=f[lc].sum+f[rc].sum;
    if(f[lc].cr==f[rc].cl) f[g].sum--;
}
void push_down(int g)
{
    if(f[g].flag)
    {
        int lc=f[g].lc,rc=f[g].rc;
        f[lc].flag=f[rc].flag=true;
        f[lc].cl=f[lc].cr=f[rc].cl=f[rc].cr=f[g].cl;
        f[lc].sum=f[rc].sum=1;
        f[g].flag=false;
    }
}
void build(int &g,int l,int r)
{
    g=++tot;
    f[g].l=l,f[g].r=r;
    if(l==r)
    {
        f[g].flag=true,f[g].sum=1;
        f[g].cl=f[g].cr=d[re[l]];
        return ;
    }
    int mid=(l+r)>>1;
    build(f[g].lc,l,mid);
    build(f[g].rc,mid+1,r);
    push_up(g);
}
void add(int g,int l,int r,int c)
{
    if(f[g].l>=l && f[g].r<=r)
        f[g].flag=true,f[g].cl=f[g].cr=c,f[g].sum=1;
    else
    {
        push_down(g);
        int mid=(f[g].l+f[g].r)>>1;
        if(r<=mid) add(f[g].lc,l,r,c);
        else if(l>mid) add(f[g].rc,l,r,c);
        else add(f[g].lc,l,mid,c),add(f[g].rc,mid+1,r,c);
        push_up(g);
    }
}
void Add(int x,int y,int c)
{
    int px=top[x],py=top[y];
    while(px!=py)
        if(deep[px]>deep[py])
            add(rt,dfn[px],dfn[x],c),x=fa[px],px=top[x];
        else add(rt,dfn[py],dfn[y],c),y=fa[py],py=top[y];
    if(dfn[x]<dfn[y]) add(rt,dfn[x],dfn[y],c);
    else add(rt,dfn[y],dfn[x],c);
}
int get(int g,int l,int r)
{
    if(f[g].l==L) a=f[g].cl;
    if(f[g].r==R) b=f[g].cr;
    if(f[g].l>=l && f[g].r<=r)
        return f[g].sum;
    else
    {
        push_down(g);
        int mid=(f[g].l+f[g].r)>>1;
        if(r<=mid) return get(f[g].lc,l,r);
        else if(l>mid) return get(f[g].rc,l,r);
        else
        {
            int ans=get(f[g].lc,l,mid)+get(f[g].rc,mid+1,r);
            if(f[f[g].lc].cr==f[f[g].rc].cl) ans--;
            return ans;
        }
    }
}
int Get(int x,int y)
{
    int ans=0;
    int px=top[x],py=top[y];
    int l_x=-1,l_y=-1;
    while(px!=py)
        if(deep[px]>deep[py])
        {
            L=dfn[px],R=dfn[x];
            ans+=get(rt,dfn[px],dfn[x]);
            x=fa[px],px=top[x];
            if(b==l_x) ans--;
            l_x=a;
        }
        else
        {
            L=dfn[py],R=dfn[y];
            ans+=get(rt,dfn[py],dfn[y]);
            y=fa[py],py=top[y];
            if(b==l_y) ans--;
            l_y=a;
        }
    if(dfn[x]>dfn[y]) swap(x,y),swap(l_x,l_y);
    L=dfn[x],R=dfn[y];
    ans+=get(rt,dfn[x],dfn[y]);
    if(a==l_x) ans--;
    if(b==l_y) ans--;
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&d[i]);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        link[u].push_back(v);
        link[v].push_back(u);
    }
    dfs1(1,0);
    top[1]=1,dfs2(1,0);
    build(rt,1,cnt);
    while(m--)
    {
        scanf("%s%d%d",s,&u,&v);
        if(s[0]==‘C‘)
        {
            scanf("%d",&c);
            Add(u,v,c);
        }
        else if(s[0]==‘Q‘)
            printf("%d\n",Get(u,v));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/hsez-cyx/p/12251219.html

时间: 2024-10-19 06:27:54

[SDOI2011]染色(树链剖分)的相关文章

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

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

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”. 请你写一个程序依次完

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”. 请你写

【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

[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

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

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

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

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

bzoj2243: [SDOI2011]染色 树链剖分

裸树剖. #include<bits/stdc++.h> using namespace std; #define N 100010 #define M (l+r>>1) #define P (k<<1) #define S (k<<1|1) #define L l,M,P #define R M+1,r,S #define Z int l=1,int r=n,int k=1 typedef int ds[N]; ds dp,num,p,size,son,t