595 字
3 分钟
ABC367 E - Permute K times 题解
2024-08-18

Atcoder Beginner Contest 367 E - Permute K times

AC Record

题目翻译#

问题陈述#

给定一个长度为 NN 的序列 XX ,其中每个元素介于 11NN 之间(含两个元素),以及一个长度为 NN 的序列 AA

打印对 AA 执行以下操作 KK 次的结果。

  • AA 替换为 BB ,使得 Bi=AXiB_i = A_{X_i}

数据范围#

  • 所有输入值均为整数。
  • 1N2×1051 \le N \le 2 \times 10^5
  • 0K10180 \le K \le 10^{18}
  • 1XiN1 \le X _ i \le N
  • 1Ai2×1051 \le A _ i \le 2 \times 10^5

解题思路#

对于这类进行单一重复操作数据范围异常大 (0K1018)(0 \le K \le 10^{18}) 的题目,我们可以考虑倍增。

TestcaseTestcase 11 为例。

原输入为:

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

我们先模拟 11 次操作:

7 2 9 3 1 5 9

在上面的基础上再模拟 11 次:

1 2 5 9 7 3 5

这样,我们就可以将两步合并为一步,跳过第 11 次的模拟,只需要 k2\dfrac k 2 次操作即可获得我们需要的数组。

继续进行合并,44 步、 88 步、 1616 步、 3232 步…… kk 次操作便能很快完成。

如果 kk 为奇数,需要先操作一次将其转换为偶数再进行倍增。

时间复杂度:O(nlogk)O(n\log _k)

倍增思想在快速幂算法、求 LCALCA (最近公共祖先) 中均有应用,可以大大降低重复操作的时间复杂度。

此外,本题还有时间复杂度为 O(n)O(n) 的图论做法,可自行阅读官方题解

完整代码#

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
int n, k;
signed main(){
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); //cin cout 优化
cin >> n >> k;
vector<int> x(n);
vector<int> y(n);
for(int i = 0; i < n; i++){
cin >> x[i];
x[i]--; //下标从0开始
}
iota(y.begin(), y.end(), 0); //将 y 赋值为0 ~ n-1
while(k > 0){
if(k % 2 == 1){ //k为奇数则操作一次
for(int i = 0; i < n; i++){
y[i] = x[y[i]];
}
}
vector<int> tmp(n);
for(int i = 0; i < n; i++){
tmp[i] = x[x[i]];
}
x = move(tmp); //将tmp直接覆盖到x上,时间复杂度 O(1)
k /= 2; //倍增
}
vector<int> a(n);
vector<int> b(n);
for(int i = 0; i < n; i++){
cin >> a[i];
}
for(int i = 0; i < n; i++){
b[i] = a[y[i]];
cout << b[i] << " \n"[i == N - 1];
}
return 0;
}

参考#

Submission #56848064 - jiangly

E - Permute K times Editorial - physics0523

3分でAtCoder Beginner Contest 367 A-E - evima lab

ABC367 E - Permute K times 题解
https://darkmodest.github.io/posts/solutions/abc367e/
作者
Dark_Modest
发布于
2024-08-18
许可协议
CC BY-NC-SA 4.0