• [codevs1566]染色


    题目描述 Description

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

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

    2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。

    请你写一个程序依次完成这m个操作。

    输入描述 Input Description

    第一行包含2个整数n和m,分别表示节点数和操作数;

    第二行包含n个正整数表示n个节点的初始颜色

    下面n-1行每行包含两个整数 和 ,表示xy之间有一条无向边。

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

    “C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;

    “Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。

    输出描述 Output Description
     对于每个询问操作,输出一行答案。
    样例输入 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

    数据范围及提示 Data Size & Hint
     对于100%的数据1≤n≤10^5,1≤m≤10^5,1≤c≤10^9;

    很明显是一道树链剖分的题,所以我们只需要考虑线性时候该怎么搞了。

    用线段树是肯定的。对于每一个节点o,维护该区间内颜色块数量是肯定的,用cnum[o]来表示。但是发现不能强行合并,因为相邻部分有可能颜色相同,算成一块。所以我们再需要维护两个信息L[o]与R[o]分别记录区间o最左端的颜色以及最右边颜色就可以了。在pushup过程中需要判断如果r[lo]==l[ro],cnum[o]--;这样的话线性做法就可以了。

    写好线段树之后,默一份树链剖分代码上去,套一个线段树,结果发现连样例都过不了!手画一下样例,发现在树链上进行操作时进行了若干次query,这些query中的相邻部分有可能颜色相同,怎么办呢,不要慌,我们再写一个名叫special的函数,专门来计算每一条重链上最上段点的颜色,在合并的时候特判一下就好了。

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<queue>
    #include<cmath>
    #include<cstring>
    using namespace std;
    typedef long long LL;
    #define mem(a,b) memset(a,b,sizeof(a))
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
        while(isdigit(c)){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    const int maxn=100010;
    int n,m,ce=-1,es,a,b,c,first[maxn],size[maxn],fa[maxn],deep[maxn],w[maxn],id[maxn],bl[maxn];
    char tp;
    int cnum[maxn<<2],L[maxn<<2],R[maxn<<2],tag[maxn<<2],col[maxn];
    struct Edge
    {
        int u,v,next;
        Edge() {}
        Edge(int _1,int _2,int _3) : u(_1),v(_2),next(_3) {}
    }e[maxn<<1];
    void addEdge(int a,int b)
    {
        e[++ce]=Edge(a,b,first[a]);first[a]=ce;
        e[++ce]=Edge(b,a,first[b]);first[b]=ce;
    }
    void dfs(int now,int pa)
    {
        size[now]=1;
        for(int i=first[now];i!=-1;i=e[i].next)
            if(e[i].v!=pa)
            {
                fa[e[i].v]=now;deep[e[i].v]=deep[now]+1;
                dfs(e[i].v,now);
                size[now]+=size[e[i].v];
            }
    }
    void divide(int now,int chain)
    {
        id[now]=++es;bl[now]=chain;
        int maxs=0;
        for(int i=first[now];i!=-1;i=e[i].next)
            if(fa[now]!=e[i].v && size[e[i].v]>size[maxs])maxs=e[i].v;
        if(!maxs)return;
        divide(maxs,chain);
        for(int i=first[now];i!=-1;i=e[i].next)
            if(fa[now]!=e[i].v && e[i].v!=maxs)divide(e[i].v,e[i].v);
    }
    void pushdown(int l,int r,int o)
    {
        if(tag[o]==-1 || l==r)return;
        int mid=(l+r)>>1,lo=o<<1,ro=lo|1;
        tag[lo]=tag[ro]=tag[o];
        cnum[lo]=cnum[ro]=1;
        L[lo]=R[lo]=L[ro]=R[ro]=L[o];
        tag[o]=-1;
    }
    void pushup(int l,int r,int o)
    {
        int mid=(l+r)>>1,lo=o<<1,ro=lo|1;
        L[o]=L[lo];R[o]=R[ro]; 
        cnum[o]=cnum[lo]+cnum[ro];
        if(R[lo]==L[ro])cnum[o]--;
    }
    void build(int l,int r,int o)
    {
        if(l==r)
        {
            cnum[o]=1;tag[o]=-1;
            L[o]=R[o]=col[l];
            return;
        }
        int mid=(l+r)>>1,lo=o<<1,ro=lo|1;
        build(l,mid,lo);build(mid+1,r,ro); 
        pushup(l,r,o);tag[o]=-1;
    }
    void update(int l,int r,int o,int a,int b,int c)
    {
        if(l==a && r==b)
        {
            tag[o]=c;cnum[o]=1;
            L[o]=R[o]=c;
            return;
        }
        pushdown(l,r,o);
        int mid=(l+r)>>1,lo=o<<1,ro=lo|1;
        if(b<=mid)update(l,mid,lo,a,b,c);
        else if(a>mid)update(mid+1,r,ro,a,b,c);
        else
        {
            update(l,mid,lo,a,mid,c);
            update(mid+1,r,ro,mid+1,b,c);
        }
        pushup(l,r,o);
    }
    int query(int l,int r,int o,int a,int b)
    {
        if(l==a && r==b)return cnum[o];
        int mid=(l+r)>>1,lo=o<<1,ro=lo|1;
        pushdown(l,r,o); 
        if(a>mid)return query(mid+1,r,ro,a,b);
        else if(b<=mid)return query(l,mid,lo,a,b);
        else
        {
            int ans=query(l,mid,lo,a,mid)+query(mid+1,r,ro,mid+1,b);
            return R[lo]==L[ro] ? ans-1 : ans;
        }    
    }
    int special(int l,int r,int o,int a)
    {
        if(l==r)return L[o];
        pushdown(l,r,o);
        int mid=(l+r)>>1,lo=o<<1,ro=lo|1;
        if(a<=mid)return special(l,mid,lo,a);
        else return special(mid+1,r,ro,a);
    }
    void paint(int a,int b,int c)
    {
        while(bl[a]!=bl[b])
        {
            if(deep[bl[a]]<deep[bl[b]])swap(a,b);
            update(1,n,1,id[bl[a]],id[a],c);
            a=fa[bl[a]];
        }
        if(id[a]>id[b])swap(a,b);
        update(1,n,1,id[a],id[b],c);
    }
    int Query(int a,int b)
    {
        int sum=0;
        while(bl[a]!=bl[b])
        {
            if(deep[bl[a]]<deep[bl[b]])swap(a,b);
            sum+=query(1,n,1,id[bl[a]],id[a]);
            if(special(1,n,1,id[bl[a]])==special(1,n,1,id[fa[bl[a]]]))sum--;
            a=fa[bl[a]];
        }
        if(id[a]>id[b])swap(a,b);
        sum+=query(1,n,1,id[a],id[b]);
        return sum;
    }
    int main()
    {
        mem(first,-1);
        n=read();m=read();
        for(int i=1;i<=n;i++)w[i]=read();
        for(int i=1;i<n;i++)a=read(),b=read(),addEdge(a,b);
        dfs(1,1);divide(1,1);
        for(int i=1;i<=n;i++)col[id[i]]=w[i];
        build(1,n,1);
        while(m--)
        {
            cin>>tp;a=read();b=read();
            if(tp=='C')c=read(),paint(a,b,c);
            else printf("%d
    ",Query(a,b));
        }
        return 0;
    }
  • 相关阅读:
    java SE连接mysql
    ubuntu_linux窗口显示文件路径方法Ctrl+L
    android studio安卓连接mysql数据库方法
    Java打开文件
    创建指定大小、指定数量的文件
    Java动态内存占用
    vite2 + vue3 开发组件库
    smallbin double linked list corrupted
    qt多线程信号槽传参不是继承QObject可能导致信号槽连接 无效 正常情况会直接报错
    postman tests常用脚本
  • 原文地址:https://www.cnblogs.com/FYH-SSGSS/p/6696582.html
Copyright © 2020-2023  润新知