题意
传送门 Codeforces 1619H Permutation and Queries
题解
若排列保持不变,可以预处理后倍增求解,但对于会改变排列的操作 1,则难以胜任。考虑分块,对每个位置 i i i,预处理出向后跳 m m m 个位置的答案 f [ j ] f[j] f[j],以及反排列 r [ p [ j ] ] = j r[p[j]] = j r[p[j]]=j。交换 x , y x,y x,y 后,对于 x x x 的改变,仅会对 x , r [ x ] , r [ r [ x ] ] , ⋯ x,r[x],r[r[x]],\cdots x,r[x],r[r[x]],⋯ 共 m m m 个位置的 f f f 产生影响( y y y 同理),那么只需要重新计算这些位置的 f f f,单次操作 1 复杂度 O ( m ) O(m) O(m)。单次操作 2 查询复杂度 O ( n / m + m ) O(n/m+m) O(n/m+m)。令 m = n m = \sqrt{n} m=n,总时间复杂度 O ( ( n + q ) n ) O\Big((n+q)\sqrt{n}\Big) O((n+q)n)。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, q;
cin >> n >> q;
vector<int> p(n);
for (int i = 0; i < n; ++i) {
cin >> p[i];
p[i] -= 1;
}
vector<int> r(n);
for (int i = 0; i < n; ++i) {
r[p[i]] = i;
}
int m = sqrt(n) + 1;
vector<int> f(n);
for (int i = 0; i < n; ++i) {
int v = i;
for (int j = 0; j < m; ++j) {
v = p[v];
}
f[i] = v;
}
while (q--) {
int op;
cin >> op;
if (op == 1) {
int x, y;
cin >> x >> y;
x -= 1, y -= 1;
swap(r[p[x]], r[p[y]]);
swap(p[x], p[y]);
auto upd = [&](int v) {
int u = v;
for (int i = 0; i < m; ++i) {
u = p[u];
}
for (int i = 0; i < m; ++i) {
f[v] = u;
v = r[v];
u = r[u];
}
};
upd(x);
upd(y);
} else {
int i, k;
cin >> i >> k;
i -= 1;
while (k >= m) {
i = f[i];
k -= m;
}
while (k > 0) {
i = p[i];
k -= 1;
}
cout << i + 1 << '\n';
}
}
return 0;
}