• [HNOI2009] 梦幻布丁


    题目描述

    解法

    每种颜色的点都可以构成一个序列,而把一种颜色改成另一种颜色,实际上是将一个序列接在另一个序列后面,同时将段数量维护一下。段数量如何维护?可以看出,对于每次修改,只有被修改的点可能会产生贡献,且只与这些点前一个和后一个点有关。如果修改了之后前后的点颜色相同,就将段数减一。事实上将 (x) 并到 (y) 和将 (y) 并到 (x) 是等价的,所以每次可以贪心地选择将少的合并到多的上面,即启发式合并。而对于这个序列,用一个链表维护就可以了。每次合并,只需要修改头指针和尾指针,并将节点少的那个链表遍历一遍,暴力修改原序列颜色。这样复杂度是 (O(nlog n)) 的。

    Tips

    题目要求将 (x) 改成 (y),但我们有时候将 (y) 改成了 (x)。所以还要维护一个 (ref[x]),表示颜色 (x) 在当前意义下指代的是哪个颜色。

    #include<stdio.h>
    #define N 1000007
    
    inline int read(){
        int x=0,flag=1; char c=getchar();
        while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
        return flag? x:-x;
    }
    
    inline void swap(int &x,int &y){x^=y,y^=x,x^=y;}
    int n,m,h[N],t[N],ans=0,nxt[N],cnt=0,sz[N],a[N],ref[N];
    int main(){
        n=read(),m=read();
        for(int i=1;i<=n;i++){
            a[i]=read(),ref[a[i]]=a[i];
            if(a[i]!=a[i-1]) ans++;
            if(!t[a[i]]) h[a[i]]=i;
            sz[a[i]]++,nxt[i]=t[a[i]],t[a[i]]=i;
        }
        while(m--){
            int opt=read();
            if(opt==1){
                int x=read(),y=read();
                if(x==y) continue;
                if(sz[ref[x]]>sz[ref[y]])
                    swap(ref[x],ref[y]);
                x=ref[x],y=ref[y];
                if(!sz[x]) continue;
                for(int i=t[x];i;i=nxt[i])
                    ans-=(a[i-1]==y)+(a[i+1]==y);
                for(int i=t[x];i;i=nxt[i]) a[i]=y;
                nxt[h[x]]=t[y],sz[y]+=sz[x],t[y]=t[x];
                t[x]=h[x]=sz[x]=0;
            }else printf("%d
    ",ans);
        }
    }
    
  • 相关阅读:
    23种设计模式
    iOS中 判断对象类型的一些方法
    iOS 反射获取类的属性列表
    CATransition--(2)
    CATransition
    App Extension编程指南(iOS8/OS X v10.10):常见问题的处理方案
    触摸、手势与响应者链条
    NSTimer使用
    监控数据库运行
    MS AX 技术相关网站收藏
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/14026188.html
Copyright © 2020-2023  润新知