Just Shuffle

Just Shuffle

题意

给你一个 1到n的排列 p 你可置换p, k次 (置换规则自己定)得到一个排列 A, 问 p置换一次是多少?

题解

现在我们只 知道 k, A, 和刚开始的p(即\(1, 2, 3 ……, n\)) 如何求出 p置换一次的结果呢?

首先你要知道几个前置知识(重点)

  1. 一个序列置换它的循环节大小的次数还是本身。
  2. 一个序列置换 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("");
}
posted @ 2020-07-16 23:59  ccsu_zhaobo  阅读(54)  评论(0编辑  收藏  举报