1. 程式人生 > 其它 >dls的資料結構—啟發式合併,dsu on tree

dls的資料結構—啟發式合併,dsu on tree

啟發式合併

有n個集合,si = {i}
每次將兩個集合sx, sy兩個集合合併,做n - 1次變成一個集合
啟發式合併就是維護每個集合是什麼
並查集是相當於只是打了一個標記,查詢的時候再把之前的標記更新好
啟發式合併是將集合真的給合併在一起,然後刪掉被合併的集合
並且保證每次都是小的集合合併到大的集合裡面
考慮每個元素的貢獻的話,他只會被合併log次,所以總共的時間複雜度是O(nlogn)
#include<bits/stdc++.h>

using namespace std;
const int N = 1e5+10, M = 1e6+10;
int ans;
vector<int> p[M];
int a[N];


int main(){
	int n, m; scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i ++){
		scanf("%d", &a[i]);
		p[a[i]].push_back(i);
	} 
	// 這裡對邊界進行了擴充,方便處理
	for(int i = 1; i <= n + 1; i ++){
		ans += a[i] != a[i - 1];
	}

	for(int i = 1; i <= m; i ++){
		int op; scanf("%d", &op);
		if(op == 1){
			int x, y; scanf("%d %d", &x, &y);
			if(x == y) continue;
                        // 這裡只維護了p[x],p[y]的正確性,而沒有維護a的正確性,因為a = {1, 2, 3} 和a = {3, 2, 1}
                        // 是沒有任何區別的,我們只需要讓a保持該不一樣的還不一樣就可以了
			if(p[x].size() > p[y].size()){
                                // O(1)的時間維護兩個vector的不同
				p[x].swap(p[y]);
			}
			if(p[y].size() == 0) continue;
			int color = a[p[y][0]];
			auto modify = [&](int item, int color){
				ans -= (a[item] != a[item - 1]) + (a[item] != a[item + 1]);
				a[item] = color;
				ans += (a[item] != a[item - 1]) + (a[item] != a[item + 1]);
			};
			for(auto item :  p[x]){
				modify(item, color);
				p[y].push_back(item);
			}
			p[x].clear();
		}
		else if(op == 2){
			printf("%d\n", ans - 1);
		}
	}
}