cf1620 E. Replace the Numbers
题意:
数组初始为空。进行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 << ' ';
}