1. 程式人生 > 其它 >[分塊] Codeforces 1619H Permutation and Queries

[分塊] Codeforces 1619H Permutation and Queries

題目大意

給你一個 \(n(1\leq n\leq 10^5)\) 個數的排列 \(p\),你需要維護以下兩種操作:

  • 1 x y :交換 \(p_x\)\(p_y\)
  • 2 i k :令 \(i:=p_i\)\(k\)次後輸出\(i\)

運算元量小於等於 \(10^5\)

題解

首先老套路對於排列 \(p\),從 \(i\)\(p_i\) 連邊,可以形成若干個有向環。然後操作1交換 \(p_x\)\(p_y\) 實際上是改變環上兩個指標的指向,可以導致一個環拆成兩個環,或者兩個環合併成一個環。所以陣列模擬連結串列可以很容易建出這些有向環,並且很容易維護操作1。但是操作2比較難維護,因為連結串列無法支援隨機訪問。然後 \(O(n\log n)\)

好像沒有什麼好做法,於是就可以想 \(O(n\sqrt n)\) 的做法了。設 \(link[u]\) 表示從 \(u\) 點出發,往下跳 \(\sqrt n\) 次所跳到的點。那麼相比於直接暴力跳 \(k\) 次,我們只需要跳 \(u:=link[u]\) 最多 \(\sqrt n\) 次,最後剩下的步數一定是小於 \(\sqrt n\) 的,可以暴力跳。於是單次查詢操作的時間複雜度是 \(O(\sqrt n)\)。對於修改操作,因為交換兩個指標可能導致 \(link[u]\) 發生變化,所以對於要修改指標的結點可以暴力回退 \(\sqrt n\) 的距離來更新 \(link[u]\)。於是本題的時間複雜度為 \(O(n\sqrt n)\)

Code

#include <bits/stdc++.h>
using namespace std;

template<typename elemType>
inline void Read(elemType& T) {
    elemType X = 0, w = 0; char ch = 0;
    while (!isdigit(ch)) { w |= ch == '-';ch = getchar(); }
    while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
    T = (w ? -X : X);
}

int nxt[100010][2], link[100010];
int n, m, q;

void update_link(int u) {
    int v = u;
    for (int i = 1;i <= m;++i) v = nxt[v][0];
    for (int i = 1;i <= m;++i) { link[u] = v; u = nxt[u][1]; v = nxt[v][1]; }
}

void update(int u, int v) {
    int x = nxt[u][0], y = nxt[v][0];
    nxt[x][1] = v; nxt[y][1] = u;
    nxt[u][0] = y; nxt[v][0] = x;
    update_link(u);
    update_link(v);
}

int query(int u, int k) {
    int step = 0;
    while (step + m <= k) { u = link[u]; step += m; }
    while (step < k) { u = nxt[u][0]; ++step; }
    return u;
}

int main() {
    Read(n);Read(q);
    m = sqrt(n);
    for (int i = 1;i <= n;++i)
        Read(nxt[i][0]);
    for (int i = 1;i <= n;++i)
        nxt[nxt[i][0]][1] = i;
    for (int i = 1;i <= n;++i)
        if (!link[i]) update_link(i);
    while (q--) {
        int opt, x, y;
        Read(opt); Read(x); Read(y);
        if (opt == 1) update(x, y);
        else printf("%d\n", query(x, y));
    }
    return 0;
}