题目简述
你有两个个长度为 n n n 的数组 a a a 和 b b b,现在执行 k k k 次以下操作:
构造一个新的序列 c c c,使得 c i = a b i c_i = a_{b_i} ci=abi,再将 a a a 设为 c c c。
输出数组 a a a 执行 k k k 次后的结果。
输入格式
输入共
3
3
3 行。
第一行两个整数
n
n
n 和
k
k
k,表示数组长度和操作次数。
第二行
n
n
n 个整数,表示数组
a
a
a。
第三行
n
n
n 个整数,表示数组
b
b
b。
n k
a1 a2 …… an
b1 b2 …… bn
输出格式
输出一行 n n n 个整数 a 1 ∼ a n a_1 \sim a_n a1∼an,以空格分开。
a1 a2 …… an
数据范围
1
≤
n
≤
2
×
1
0
5
1 \le n \le 2 \times 10^5
1≤n≤2×105
0
≤
k
≤
2
18
0 \le k \le 2^{18}
0≤k≤218
1
≤
a
i
≤
2
×
1
0
5
1 \le a_i \le 2 \times 10^5
1≤ai≤2×105
1
≤
b
i
≤
n
1 \le b_i \le n
1≤bi≤n
思路
最先想到的就是直接进行 k k k 次操作,一定会超时。
k k k 很大,因此我们可以考虑将其拆分成几段进行计算。
因此我们会想到 s t st st 表。
我们先忽略 a a a 里面存的值,将下标记在 t t t 数组里。
进行
1
1
1 次操作,
t
t
t 变为
b
1
,
b
2
,
…
,
b
n
b_1,b_2,\dots,b_n
b1,b2,…,bn。
进行
2
2
2 次操作,
t
t
t 变为
b
b
1
,
b
b
2
,
…
,
b
b
n
b_{b_1}, b_{b_2}, \dots,b_{b_n}
bb1,bb2,…,bbn,相当于两次
1
1
1 操作。
进行
3
3
3 次操作,相当于
1
+
2
1+ 2
1+2 操作。
进行
4
4
4 次操作,相当于两次
2
2
2 操作。
用
s
t
i
,
j
st_{i,j}
sti,j 表示从第
i
i
i 个位置,走
2
j
2^j
2j 步走到那个位置。
当
j
>
0
j > 0
j>0 时,
s
t
i
,
j
=
s
t
s
t
i
,
j
−
1
,
j
−
1
st_{i,j} = st_{st_{i, j - 1}, j - 1}
sti,j=ststi,j−1,j−1。
当
j
=
0
j = 0
j=0 时,
s
t
i
,
0
=
b
i
st_{i,0} = b_i
sti,0=bi。
当计算 i i i 位置执行 k k k 次操作后下标的值时,先把 k k k 进行二进制分解,如果第 j j j 位为 1 1 1,则 i = s t i , j i = st_{i, j} i=sti,j。
最后输出 a i a_i ai 即可。
#include<bits/stdc++.h>
using namespace std;
long long n, k;
<s 表示 st 数组,f 是预处理 2^i 的值,p 记录 k 二进制分解后的值>
<ans 记录第 i 个位置执行 k 次后的值>
long long a[200010];
long long s[64][200010];
long long p[64], f[64];
int ans[200010];
int main(){
scanf("%lld %lld", &n, &k);
for(int i = 1; i <= n; ++ i){
scanf("%lld", &s[0][i]);
}
for(int i = 1; i <= n; ++ i){
scanf("%lld", &a[i]);
}
for(int i = 1; i < 63; ++ i){
for(int j = 1; j <= n; ++ j){
s[i][j] = s[i - 1][s[i - 1][j]];//走 2^j 步相当于走两次 2^(j-1) 步
}
}
<二进制分解>
f[0] = 1;
for(int i = 1; i < 63; ++ i){
f[i] = f[i - 1] * 2;
}
for(int i = 63; i >= 0; -- i){
if(k >= f[i]){
k -= f[i];
p[i] = 1;
}
}
for(int i = 1; i <= n; ++ i){
ans[i] = i;//最初位置都为 i
}
for(int i = 0; i < 63; ++ i){
if(!p[i]){
continue;
}
//第 i 位为 1
for(int j = 1; j <= n; ++ j){
ans[j] = s[i][ans[j]];
}
}
for(int i = 1; i <= n; ++ i){
printf("%lld ", a[ans[i]]);
}
return 0;
}