[分块] 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;
}
posted @ 2022-01-09 13:25  AE酱  阅读(73)  评论(0编辑  收藏  举报