启发式合并的大概内容就是:把小的数据结构按照这个数据结构的正常插入方法,一个一个地暴力塞进去。就是在合并的时候将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();
}
}
}