• BZOJ1483 [HNOI2009]梦幻布丁


    题目描述:

    N个布丁摆成一行,进行M次操作.

    每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.

    例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.

    题解:

    链表加启发式合并。

    对每个颜色开个链表记录这个颜色每个布丁的位置,然后启发式合并链表,每次小的往大的合并,设小的链表大小x,

    每次合并复杂度是O(x),维护答案复杂度是O(x),合并后规模至少是2x,算一下复杂度就知道是O(nlogn)的怎么用启发式合并呢?

    要把链表长度小的接在链表长度大的后面,才能做到nlogn。因为把链表长度小的接在大的后面,新链表长度一定>=原长度小的链表的长度的两倍,

    最多变长logn次,所以均摊下来每次修改效率O(logn),总复杂度为O(nlogn)。

    那交换之后颜色换反了怎么办?

    记录一下每种颜色真实颜色是什么,如果一次染色是将一个大链表染向一个小链表,那就将两种颜色的真实颜色交换,仍然将小链表往大链表合并即可。

    附上代码:

    #include<cstdio>
    int n,m,a[100001],f[1000001],s[1000001],k,c,d,ans,head[1000001],next[1000001],l[1000001];
    void merge(int x,int y)
    {
        for(int i=head[x];i;i=next[i])
        {
            if(a[i+1]==y)
                ans--;
            if(a[i-1]==y)
                ans--;    
        }
        for(int i=head[x];i;i=next[i])
            a[i]=y;
        next[l[x]]=head[y];
        head[y]=head[x];
        s[y]+=s[x];
        s[x]=0;
        head[x]=0;
        l[x]=0;    
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            f[a[i]]=a[i];
            if(a[i]!=a[i-1])
                ans++;
            if(head[a[i]]==0)
                l[a[i]]=i;
            s[a[i]]++;
            next[i]=head[a[i]];
            head[a[i]]=i;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&k);
            if(k==1)
            {
                scanf("%d%d",&c,&d);
                if(c==d)
                    continue;
                if(s[f[c]]>s[f[d]])
                {
                    int z=f[d];
                    f[d]=f[c];
                    f[c]=z;
                }
                if(s[f[c]]==0)
                    continue;
                s[f[d]]+=s[f[c]];
                s[f[c]]=0;
                merge(f[c],f[d]);
            }
            else
                printf("%d
    ",ans);
        }    
        return 0;
    }
  • 相关阅读:
    linux crontab 定时使用方法
    crontab 选择编辑器 select-editor
    设置定时任务为每天凌晨2点执行和每小时执行一次
    性能测试工具--SIEGE安装及使用简介 siege压力测试
    Vue基础
    使用 supervisor 管理进程
    长按listview弹出选项列表对话框
    左右滑动弹窗之间短信内容区域来显示上一条和下一条短信
    在开机广播中启动服务
    Android spinner 样式及其使用详解
  • 原文地址:https://www.cnblogs.com/jiangminghong/p/9811343.html
Copyright © 2020-2023  润新知