zoukankan      html  css  js  c++  java
  • BZOJ-2243【SDOI2011】染色&洛谷P2486(线段树区间合并+树链剖分)

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2243

    洛谷:

    时间限制1.00s
    内存限制125.00MB
     
    BZOJ:
    Time Limit: 20 Sec  Memory Limit: 512 MB

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


    一开始题目看错了。。。纠正姿势后,emmmm,连续段的话,我们应该能想到线段树的区间合并,撇开树状图形来讲,我们应该可以很快写出序列的区间段数程序,就是裸的线段树区间合并。但此题变成了树状图形,所以我们需要用到树剖,但需要值得注意的是,线段树区间合并完之后的区间询问还需要去重,有点麻烦,加入树剖之后节点在向上爬的过程中也会重复,也就是说,我们需要两次去重,一次是线段树询问时候去重,一次是树剖爬的过程中也需要去重。

     记得之前提到过线段树区间合并的操作,我们用lcol和rcol记录节点的最左边的颜色和最右边的颜色,如果左边节点的rcol与右边节点的lcol相等,那么push_up的时候我们就将左右节点的值sum相加后-1:

    inline void push_up(int rt)
    {
        tree[rt].sum=tree[ls].sum+tree[rs].sum;
        tree[rt].lc=tree[ls].lc;tree[rt].rc=tree[rs].rc;
        if (tree[ls].rc==tree[rs].lc) tree[rt].sum--;
    }

     接下来就是就是修改操作,将一个连续的块全部赋值为x,那么我们直接打标记就好了,然后就是push_down操作,这个也比较简单,如果一个节点存在标记,那么他的左右儿子的sum=1,且col全部染成同一个颜色:

    inline void push_down(int rt)
    {
        tree[ls].f=tree[rs].f=1;
        tree[ls].sum=tree[rs].sum=1;
        tree[ls].lc=tree[ls].rc=tree[rt].lc;
        tree[rs].lc=tree[rs].rc=tree[rt].lc;
        tree[rt].f=0;
    }

    那么update就很容易写出来了:

    inline void update(int l,int r,int rt,int L,int R,int col)
    {
        if (l>=L && r<=R){
            tree[rt].lc=tree[rt].rc=col;
            tree[rt].sum=tree[rt].f=1;
            return;
        }
        if (tree[rt].f) push_down(rt);
        int mid=(l+r)>>1;
        if (mid>=L) update(lson,L,R,col);
        if (mid<R) update(rson,L,R,col);
        push_up(rt);
    }

    比较难搞的就是query操作了,这个需要跨区间合并,我们可以这么写,对于一个节点,如果它的左儿子完全不在询问范围内,则往右儿子找,如果右儿子完全不在询问范围,则往左儿子找,否则的话说明左右儿子一定都有一部分在询问区间中,那么我们递归寻找相加后,判断如果左儿子的rcol==右儿子的lcol那么也就是说中间一块是连续的,sum--:

    inline int query(int l,int r,int rt,int L,int R)
    {
        if (l>=L && r<=R){
            if (l==L) LC=tree[rt].lc;
            if (r==R) RC=tree[rt].rc;
            return tree[rt].sum;
        }
        int mid=(l+r)>>1;
        if (tree[rt].f) push_down(rt);
        if (mid>=R) return query(lson,L,R);
        else if (mid<L) return query(rson,L,R);
        int ans=query(lson,L,R)+query(rson,L,R);
        if (tree[ls].rc==tree[rs].lc) ans--;
        return ans;
    }

    写完线段树区间合并之后就是裸的跑树剖的过程了,两遍dfs,然后区间修改,区间查询,前三个就不多说了,套个板子就OK了,区间查询爬坡的时候我们还要在一次去重,这个就非常麻烦了。。。。卡了我挺久的。。 

    我们用LC和RC记录每次爬完坡之后的线段树最左边和最右边的颜色,其实也是每次树剖链的最上层和最底层,我们再用s1和s2分别记录两个不同路径的最上层,s1记录当前爬坡路径,s2记录交换路径,如果下一次爬完坡的时候RC==s1,也就是上一次的最上层和本次的最底层颜色一样,那么ans--。

    当两点的top值一样的时候注意这里的写法和一般的树剖不太一样了,top值一样就跳出爬坡过程,在id[l]和id[r]之间query,但在此之前我们需要对l,r的深度进行判断,然后考虑是否交换l和r:

    inline int query_range(int l,int r)
    {
        int s=0,s1=-1,s2=-1;
        while (top[l]!=top[r]){
            if (deep[top[l]]<deep[top[r]]) swap(l,r),swap(s1,s2);
            s+=query(1,n,1,id[top[l]],id[l]);
            if (RC==s1) s--;
            s1=LC;
            l=fa[top[l]];
        }
        if (deep[l]<deep[r]) swap(l,r),swap(s1,s2);
        s+=query(1,n,1,id[r],id[l]);//l必须放在后面,l为底层,要和上面保存一致
        if (RC==s1) s--;
        if (LC==s2) s--;
        return s;
    }
    这里需要注意的是top值相等跳出来之后,我们爬坡的顺序和之前的应该一样,将l放在底部,这样才能保证s1和s2不出差错!
    接下来又是去重了,RC==s1没什么好说的,之前说过了,但LC==s2是个什么呢?LC是最前端的,s2是交换路径后的最前端,如图所示:

     以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    #define lson l,mid,rt<<1
    #define rson mid+1,r,rt<<1|1
    #define ls rt<<1
    #define rs rt<<1|1
    
    const int mac=1e5+10;
    
    struct Edge
    {
        int to,next;
    }eg[mac<<1];
    struct Tree
    {
        int lc,rc,sum,f;
    }tree[mac<<2];
    int n,top[mac],head[mac],son[mac],dson[mac],tot=0;
    int num=0,fa[mac],deep[mac],a[mac],id[mac],w[mac];
    
    inline void add(int u,int v)
    {
        eg[++num]=Edge{v,head[u]};
        head[u]=num;
    }
    
    inline void dfs1(int x,int f)
    {
        son[x]=1;fa[x]=f;
        for (int i=head[x]; i!=-1; i=eg[i].next){
            int v=eg[i].to;
            if (v==f) continue;
            deep[v]=deep[x]+1;
            dfs1(v,x);
            son[x]+=son[v];
            if (son[dson[x]]<son[v]) dson[x]=v;
        }
    }
    
    inline void dfs2(int x,int tp)
    {
        top[x]=tp,id[x]=++tot,w[tot]=a[x];
        if (!dson[x]) return;
        dfs2(dson[x],tp);
        for (int i=head[x]; i!=-1; i=eg[i].next){
            int v=eg[i].to;
            if (v==dson[x] || v==fa[x]) continue;
            dfs2(v,v);
        }
    }
    
    inline void push_up(int rt)
    {
        tree[rt].sum=tree[ls].sum+tree[rs].sum;
        tree[rt].lc=tree[ls].lc;tree[rt].rc=tree[rs].rc;
        if (tree[ls].rc==tree[rs].lc) tree[rt].sum--;
    }
    
    inline void build(int l,int r,int rt)
    {
        if (l==r){
            tree[rt].sum=1;
            tree[rt].lc=tree[rt].rc=w[l];
            return;
        }
        int mid=(l+r)>>1;
        build(lson);build(rson);
        push_up(rt);
    }
    
    inline void push_down(int rt)
    {
        tree[ls].f=tree[rs].f=1;
        tree[ls].sum=tree[rs].sum=1;
        tree[ls].lc=tree[ls].rc=tree[rt].lc;
        tree[rs].lc=tree[rs].rc=tree[rt].lc;
        tree[rt].f=0;
    }
    
    inline void update(int l,int r,int rt,int L,int R,int col)
    {
        if (l>=L && r<=R){
            tree[rt].lc=tree[rt].rc=col;
            tree[rt].sum=tree[rt].f=1;
            return;
        }
        if (tree[rt].f) push_down(rt);
        int mid=(l+r)>>1;
        if (mid>=L) update(lson,L,R,col);
        if (mid<R) update(rson,L,R,col);
        push_up(rt);
    }
    
    inline void update_range(int l,int r,int col)
    {
        while (top[l]!=top[r]){
            if (deep[top[l]]<deep[top[r]]) swap(l,r);
            update(1,n,1,id[top[l]],id[l],col);
            l=fa[top[l]];
        }
        if (deep[l]>deep[r]) swap(l,r);
        update(1,n,1,id[l],id[r],col);
    }
    
    int LC,RC;
    
    inline int query(int l,int r,int rt,int L,int R)
    {
        if (l>=L && r<=R){
            if (l==L) LC=tree[rt].lc;
            if (r==R) RC=tree[rt].rc;
            return tree[rt].sum;
        }
        int mid=(l+r)>>1;
        if (tree[rt].f) push_down(rt);
        if (mid>=R) return query(lson,L,R);
        else if (mid<L) return query(rson,L,R);
        int ans=query(lson,L,R)+query(rson,L,R);
        if (tree[ls].rc==tree[rs].lc) ans--;
        return ans;
    }
    
    inline int query_range(int l,int r)
    {
        int s=0,s1=-1,s2=-1;
        while (top[l]!=top[r]){
            if (deep[top[l]]<deep[top[r]]) swap(l,r),swap(s1,s2);
            s+=query(1,n,1,id[top[l]],id[l]);
            if (RC==s1) s--;
            s1=LC;
            l=fa[top[l]];
        }
        if (deep[l]<deep[r]) swap(l,r),swap(s1,s2);
        s+=query(1,n,1,id[r],id[l]);//l必须放在后面,l为底层,要和上面保存一致
        if (RC==s1) s--;
        if (LC==s2) s--;
        return s;
    }
    
    inline void in(int &x)
    {
        int f=0;
        char ch=getchar();
        while (ch>'9' || ch<'0') ch=getchar();
        while (ch<='9' && ch>='0') f=(f<<1)+(f<<3)+ch-'0',ch=getchar();
        x=f;
    }
    
    inline void debug_dfs()
    {
        for (int i=1; i<=n; i++)
            printf ("%d:%d %d %d %d
    ",i,id[i],deep[i],top[i],w[i]);
    }
    
    inline void debug_tree(int l,int r,int rt,int dp)
    {
        printf("%d: %d %d %d
    ", dp,tree[rt].sum,tree[rt].lc,tree[rt].rc);
        if (l==r) return;
        int mid=(l+r)>>1;
        debug_tree(lson,dp+1);debug_tree(rson,dp+1);
    }
    
    int main()
    {
        //freopen("in.txt","r",stdin);
        memset(head,-1,sizeof head);
        int m;
        in(n);in(m);
        for (int i=1; i<=n; i++) in(a[i]);
        for (int i=1; i<n; i++) {
            int u,v;
            in(u);in(v);
            add(u,v);add(v,u);
        }
        dfs1(1,1);dfs2(1,1);
        build(1,n,1);
        //debug_dfs();
        //debug_tree(1,n,1,0);
        for (int i=1; i<=m; i++){
            char s[5];
            int l,r,w;
            scanf ("%s%d%d",s,&l,&r);
            if (s[0]=='C'){
                in(w);
                update_range(l,r,w);
            }
            else {
                int ans=query_range(l,r);
                printf ("%d
    ",ans);
            }
        }
        return 0;
    }
    View Code
    路漫漫兮
  • 相关阅读:
    怎样从外网访问内网Django?
    怎样从外网访问内网Jboss?
    怎样从外网访问内网php-fpm?
    python中关于发邮件的示例
    python中关于局部变量与全局变量的认识
    python实现二分查找与冒泡排序
    自动化测试框架中关于selenium api的二次封装
    python 的日志相关应用
    python中关于字符串的操作
    EBS 物料单位换算
  • 原文地址:https://www.cnblogs.com/lonely-wind-/p/12024454.html
Copyright © 2011-2022 走看看