E - Permute K times

E - Permute K times

Problem Statement

You are given a sequence X of length N where each element is between 1 and N, inclusive, and a sequence A of length N.
Print the result of performing the following operation K times on A.

  • Replace A with B such that Bi=AXi.

Constraints

  • All input values are integers.
  • 1N2×105
  • 0K1018
  • 1XiN
  • 1Ai2×105

Input

The input is given from Standard Input in the following format:

N K
X1 X2 XN
A1 A2 AN

Output

Let A be the sequence A after the operations. Print it in the following format:

A1 A2 AN


Sample Input 1

7 3
5 2 6 3 1 4 6
1 2 3 5 7 9 11

Sample Output 1

7 2 3 5 1 9 3

In this input, X=(5,2,6,3,1,4,6) and the initial sequence is A=(1,2,3,5,7,9,11).

  • After one operation, the sequence is (7,2,9,3,1,5,9).
  • After two operations, the sequence is (1,2,5,9,7,3,5).
  • After three operations, the sequence is (7,2,3,5,1,9,3).

Sample Input 2

4 0
3 4 1 2
4 3 2 1

Sample Output 2

4 3 2 1

There may be cases where no operations are performed.


Sample Input 3

9 1000000000000000000
3 7 8 5 9 3 7 4 2
9 9 8 2 4 4 3 5 3

Sample Output 3

3 3 3 3 3 3 3 3 3

 

解题思路

  比赛时一直想着线性的做法,完全没考虑过倍增,结果发现代码巨难写。

  首先如果按照 ixi 进行连边,就会得到一棵基环树,当 k 足够大的时候其实就是在环上不断循环。对于每个点可以先让 k 减去该点到环的距离,然后对环的大小取模,求出最后停留在环上的哪个位置,但实现起来非常复杂。本质上就是快速求出每个点在走 k 步后最后停留在哪里,由于 k 非常大这启发我们可以往倍增上考虑。

  定义 Xij 表示从点 i 经过 2j 步后到达的位置,Xi0 就是初始输入的数据。想要求得 Xij,我们可以先让 i2j1 步到达 Xij1,再从 Xij12j1 步到达 Xij,因此有转移方程 Xij=XXij1j1

  为了求出从 ik 步的结果,先把 k 看作二进制数 k59k58k0,kj{0,1},维护一个数组 pi 表示从 i 经过若干步后到达的位置,初始时定义 pi=i。然后枚举 k 的每一个二进制位,如果 kj=1,意味着我们需要走 2j 步,更新 pi=Xpij

  最后输出 api 即可。

  AC 代码如下,时间复杂度为 O(nlogk)

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 2e5 + 5;

int x[60][N], p[N], a[N];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    LL m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> x[0][i];
    }
    for (int i = 1; 1ll << i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            x[i][j] = x[i - 1][x[i - 1][j]];
        }
    }
    iota(p + 1, p + n + 1, 1);
    for (int i = 0; 1ll << i <= m; i++) {
        if (m >> i & 1) {
            for (int j = 1; j <= n; j++) {
                p[j] = x[i][p[j]];
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++) {
        cout << a[p[i]] << ' ';
    }
    
    return 0;
}

 

参考资料

  Editorial - AtCoder Beginner Contest 367:https://atcoder.jp/contests/abc367/editorial/10750

posted @   onlyblues  阅读(49)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2022-08-18 玉米田
Web Analytics
点击右上角即可分享
微信分享提示