1 条题解

  • 0
    @ 2025-8-24 21:46:26

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Siyuan
    Dream OIer.

    搬运于2025-08-24 21:46:26,当前版本为作者最后更新于2018-11-25 11:59:04,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    $$\Large\texttt{My Blog}$$


    题目链接:BZOJ 1483

    nn 个布丁摆成一行,每个布丁都有一个颜色 aia_i,进行 mm 次操作。操作共有 22 种:

    • 将颜色为 xx 的布丁全部变成颜色 yy 的布丁。
    • 询问当前一共有多少段颜色(例如颜色分别为 1,2,2,11,2,2,144 个布丁一共有 33 段颜色)。

    数据范围:1n,m1051\leqslant n,m\leqslant 10^50<ai,x,y<1060<a_i,x,y<10^6


    Solution

    我们可以把每种颜色的布丁集合想象成是一个队列。那么就有若干个长度总和为 nn 的队列,每次操作需要合并 xxyy。如果我们暴力合并,那么合并一次复杂度最坏为 O(n)O(n)

    但是有个叫做启发式合并的东西!它的本质很简单:每次把短的合并到长的上面,那么合并一次的复杂度为 O(短的队列)O(|短的队列|)。这样看上去貌似没有什么差别嘛 QAQ,接下来我们分析一下均摊复杂度。

    考虑用贡献法来分析。我们令两个集合的分别为 AABB,且 A<B|A|<|B|,那么我们把 AA 暴力加入到 BB 中。那么 AA 中的元素所在的集合大小变成 A+B|A|+|B|,也就是说至少变成了原来的两倍。所以每个元素至多被加入 logn\log n 次,总的复杂度为 O(nlogn)O(n\log n)

    对于这道题目,我们先求出原序列的答案,对于每一种颜色都用类似链表的数据结构串起来,并记录下尾节点。每次修改,都根据启发式合并的方法来暴力合并,然后处理一下此次合并对答案的影响(显然答案是不增的)。

    但是如果我们把 11 染成 22 并且 S1>S2|S_1|>|S_2|,那么我们应该把 22 接到 11 的后面。这样会有一个问题:本次修改后这个链的颜色是 11(颜色为 22 的链被删除了),如果接下来修改颜色 22(显然这是合法的),会使得找不到颜色 22 而只能找到颜色 11 了。所以我们需要使用一个 ff 数组,表示当我们要寻找颜色 xx 时,实际上需要寻找颜色为 f[x]f[x] 的链。如果遇到上面这种情况就要交换交换 f[x]f[x]f[y]f[y]

    时间复杂度O(nlogn)O(n\log n)


    Code

    #include <iostream>
    #include <cstdio>
    
    const int N=1e5+5,M=1e6+5;
    int n,m,c[N],sz[M],st[M],f[M],hd[M],nxt[N],ans;
    
    void merge(int x,int y) {
        for(int i=hd[x];i;i=nxt[i]) ans-=(c[i-1]==y)+(c[i+1]==y);
        for(int i=hd[x];i;i=nxt[i]) c[i]=y;
        nxt[st[x]]=hd[y],hd[y]=hd[x],sz[y]+=sz[x];
        hd[x]=st[x]=sz[x]=0;
    }
    int main() {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i) {
            scanf("%d",&c[i]),f[c[i]]=c[i];
            ans+=c[i]!=c[i-1];
            if(!hd[c[i]]) st[c[i]]=i;
            ++sz[c[i]],nxt[i]=hd[c[i]],hd[c[i]]=i;
        }
        while(m--) {
            int opt;
            scanf("%d",&opt);
            if(opt==2) printf("%d\n",ans);
            else {
                int x,y;
                scanf("%d%d",&x,&y);
                if(x==y) continue;
                if(sz[f[x]]>sz[f[y]]) std::swap(f[x],f[y]);
                if(!sz[f[x]]) continue;
                merge(f[x],f[y]);
            }
        }
        return 0;
    }
    
    • 1

    信息

    ID
    2274
    时间
    1000ms
    内存
    128MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者