[分塊] Codeforces 1619H Permutation and Queries
阿新 • • 發佈:2022-01-10
題目大意
給你一個 \(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)\)
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; }