启发式合并

211 阅读2分钟

启发式合并的大概内容就是:把小的数据结构按照这个数据结构的正常插入方法,一个一个地暴力塞进去。就是在合并的时候将size小的那个集合合并到size大的那个集合里面。初看上可能感觉这就是个暴力。但是我们分析一下每个元素被push_back() 了多少次。

一个集合中的元素被放入另一个集合中会被push_back() 一次。但是这个元素所在的集合的大小至少扩大了一倍 所以时间复杂度变成了log级别

题目:

n个布丁摆成一行,进行m次操作。每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色。

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

输入格式

第一行两个整数n,m(1≤n,m≤105)表示布丁的个数和操作次数。

第二行n个数a1,a2,…,an(1≤ai≤106)表示第i个布丁的颜色。

接下来m行,每行描述一次操作。每行首先有一个整数op表示操作类型:

  • 若op=1,则后有两个整数x,y,表示将颜色x的布丁全部变成颜色y。保证1≤x,y≤106,x可能等于y
  • 若op=2,则表示一次询问。

输出格式

针对第二类操作即询问,依次输出当前有多少段颜色。

样例输入

4 3
1 2 2 1
2
1 2 1
2

样例输出

3
1

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int n,m,a[N],ans;
vector<int> pos[N];
int main()
{
    cin >> n >> m;
    for(int i = 1;i <= n;i ++)
    {
        cin >> a[i];
        pos[a[i]].push_back(i);
    }

    for(int i = 1;i <= n + 1;i ++)
    {
        ans += a[i] != a[i-1];
    }
    for(int i = 0;i < m;i ++)
    {
        int op;
        cin >> op;
        if(op == 2)
        {
            cout << ans-1 << '\n';
        }
        else
        {
            int x,y;
            cin >> x >> y;
            if(x == y)
                continue;
            if(pos[x].size() > pos[y].size())
            {
                pos[x].swap(pos[y]);
            }
            if(pos[y].empty())
                continue;
            auto modify = [&](int p,int col)
            {
                ans -= (a[p]!=a[p-1]) + (a[p] != a[p+1]);
                a[p] = col;
                ans += (a[p]!=a[p-1]) + (a[p] != a[p+1]);
            };
            int col = a[pos[y][0]];
            for(int p:pos[x]){
                modify(p,col);
                pos[y].push_back(p);
            }
            pos[x].clear();
        }

    }
}