Just Shuffle
Just Shuffle
题意
给你一个 1到n的排列 p 你可置换p, k次 (置换规则自己定)得到一个排列 A, 问 p置换一次是多少?
题解
现在我们只 知道 k, A, 和刚开始的p(即\(1, 2, 3 ……, n\)) 如何求出 p置换一次的结果呢?
首先你要知道几个前置知识(重点)
- 一个序列置换它的循环节大小的次数还是本身。
- 一个序列置换 k次, 为 \(ans[v[i]] = v[(i + k)\%len]\) 其中 \(ans\) 为置换k次的答案, \(v\) 为置换规则, \(len\) 为循环节的大小, 也就是 求出的环大小。
ok, 下一步就是 解题思路:
因为 p 置换 k次后 得到 A, 设 \(t\) 为有效的置换次数, \(t = k \%len\) (\(len\) 循环节大小也就是环的大小)
因为 \(p到k置换了 t次\)
设 \(x\) 为 按照 \(p到 A的规则置换x次\)
所以只要求出, \(x * t \% len == 1\)
就可以得到 p置换一次的答案了。
因为 x小于等于 len - 1, 所以直接枚举就好了。
include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 7;
typedef long long ll;
int a[N], n, vis[N], ans[N];
ll k;
vector<int> v;
int main() {
scanf("%d %lld", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; i++) {
if(vis[a[i]] == 0) {
v.clear();
int now = a[i];
while(vis[now] == 0) {
vis[now] = 1;
v.push_back(now);
now = a[now];
}
int len = v.size();
int niv;
for(int i = 1; i < len; i++) {
if((i % len * k % len)%len == 1) {
niv = i;
break;
}
}
for(int i = 0; i < v.size(); i++) {
ans[v[i]] = v[(i + niv) % len];
}
}
}
for (int i = 1; i <= n; i++) {
printf("%d ", ans[i]);
}
puts("");
}