Codeforces 1619H 分块

题意

传送门 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值