Permutation Swaps
Permutation Swaps
题目理解
第一个操作:把第i个数移到位置p[i](1<=i<=n)
发现:这个操作其实就是循环移位,有Teleporter的经验在前,此操作可用倍增\(\log_{2}^{n}\)实现,同时可	以推出此操作可以叠加,所以用一个sum记录操作次数,查询时一次解决
第二个操作:交换两个位置,此操作较复杂,详见后文
第三个操作:查询x位置数的值
结论:在不考虑第二个操作的情况下,对sum进行二进制拆分,并用预处理好的\(f_k(u)\)[1]更新x
做法
因为第二个操作需要改变原序列位置,所以改变后直接查询\(f_k\)函数会错,这时,就需要运用逆思想:正着不行,倒着来。
在这里,我们需要用到一个数学结论
\[当f是集合A到集合B的一个满映射时\\
\exists B到A的映射g使得g(f(i))=i\\
我们称g是f的反函数
\]
 我们对\(p\)处理出它的反函数\(g\),并对其处理倍增ST表,我们可以发现查询\(f_k\)时查询的是位置,所以做第二个操作时,交换的不是 x 和 y ,而是\(ST_k(x)\)和\(ST_k(y)\)。并且因为\(f_k\)查询的是位置,于是我们直接交换这两个值不影响结果。
 预处理:对于\(f\)我们可以发现\(f_k(u)=f_{k-x}(f_x(u))\),所以对于\(\forall\)u预处理\(f_{2^b}(u)\)(b<=\(\log_{2}^{n}\)),计算\(f_k(u)\)时,对k进行二进制拆分并更新。例如:对于\(k=13\),可以写成\(13=8+4+1\),于是有\(f_{13}(u)=f_8(f_4(f_1(u)))\)。
 最后看实现
code:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5, K = 17;
int t, n, a[N], p[N];
int g[N], ST[K + 5][N];
int q, op, sum;
inline void init() {
    sum = 0;
    for (int i = 1;i <= n;i++) g[p[i]] = i;
    for (int i = 1;i <= n;i++) ST[0][i] = g[i];
    for (int i = 1;i < K;i++)
        for (int j = 1;j <= n;j++)
            ST[i][j] = ST[i - 1][ST[i - 1][j]];
}
inline int getback(int x, int k) {
    for (int i = K - 1;i >= 0;i--)
        if ((k >> i) & 1) x = ST[i][x];
    return x;
}
inline void solve() {
    if (op == 1) sum++;
    if (op == 2) {
        int x, y;
        scanf("%d%d", &x, &y);
        int gx = getback(x, sum), gy = getback(y, sum);
        swap(a[gx], a[gy]);
    }
    if (op == 3) {
        int x;
        scanf("%d", &x);
        int gx = getback(x, sum);
        printf("%d\n", a[gx]);
    }
}
int main() {
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        for (int i = 1;i <= n;i++) scanf("%d", &a[i]);
        for (int i = 1;i <= n;i++) scanf("%d", &p[i]);
        init();
        scanf("%d", &q);
        while (q--) {
            scanf("%d", &op);
            solve();
        }
    }
    return 0;
}
位置为u的跳2k次 ↩︎

                
            
        
浙公网安备 33010602011771号