cf1620 E. Replace the Numbers
阿新 • • 發佈:2022-04-11
題意:
陣列初始為空。進行q次兩種操作:
- 1 x 在陣列末尾加一個數x
- 2 x y 把現存的所有x變成y
輸出最後的陣列。
思路:
法一:啟發式合併,線上
考慮一種暴力做法,記錄每個值 val 出現的所有位置 pos[val],操作2就把所有 pos[x] 裡的位置丟進 pos[y] 裡面。
啟發式合併,每次把小的push到大的裡面。複雜度 \(nlogn\)
int q; cin >> q; while(q--) { int t, x, y; cin >> t; if(t == 1) cin >> x, pos[x].pb(++n); else { cin >> x >> y; if(x != y) { //注意判斷 if(pos[x].size() > pos[y].size()) //讓y是比較大的 swap(pos[x], pos[y]); //swap的複雜度是O1的 for(int i : pos[x]) pos[y].pb(i); pos[x].clear(); } } } for(int i = 1; i < N; i++) for(int p : pos[i]) ans[p] = i; for(int i = 1; i <= n; i++) cout << ans[i] << ' ';
法二:離線,倒序處理操作
注意到每次操作會對所有前面的(而不是後面的)位置造成影響。倒序處理操作,p[x] 表示 x 要變成 p[x]。
複雜度線性。注意這完全不是並查集!
const signed N = 5e5 + 3; int p[N]; signed main() { iofast; vector<tuple<int,int,int>> query; int q; cin >> q; while(q--) { int t, x, y=0; cin >> t; if(t == 1) cin >> x; else cin >> x >> y; query.pb({t,x,y}); } reverse(all(query)); for(int i = 1; i < N; i++) p[i] = i; vector<int> ans; for(auto &[t,x,y] : query) if(t == 1) ans.pb(p[x]); else p[x] = p[y]; reverse(all(ans)); for(int x : ans) cout << x << ' '; }