dls的資料結構—啟發式合併,dsu on tree
阿新 • • 發佈:2022-04-22
啟發式合併
有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); } } }