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 << ' ';
}

posted @ 2022-04-10 23:12  Bellala  阅读(27)  评论(0)    收藏  举报