cf1553E. Permutation Shift

Posted Jozky86

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cf1553E. Permutation Shift相关的知识,希望对你有一定的参考价值。

cf1553E. Permutation Shift

题意:

给出一个1到n的排列,每次可以交换两个数,问在交换最多m次(m <= n/3)之后能不能得到由1 2 3 … n循环右移所得到的的排列,输出所有能得到的排列和循环右移的次数。
数据范围:n <= 3e5

题解:

如何求每次置换操作后的数组?
每次置换操作k相当于b[i]=(i-k+n)%n

我们逆向考虑,对于每次移动k,来判断是否可以通过最多m次操作回到原本排列(原本1到n的排列)。
我们最终的目的是想要n个自环。假设当前b中有t个环,每次交换都可以使得自环的个数+1,所以至少需要n-t次交换才可以达到目的,也就是如果m>=n-t即k符合要求

显然每次检测都是O(n)的,我们不可能对每个K都检测一遍,总复杂度就是O(n^2)
最多可以交换m次,那么最多就会影响到2 * m个环,也就是初始就最少有n-2 * m个环才行。
我们设num[i]表示偏移量为i时的自环个数
num[i]要>=n-2m
Σnum[i]=n,m<=n/3,
所以num[i]>=n/3,说明这样的k最多有3个
所以最多三次O(n)计算最小交换次数
总复杂度O(n)

代码:

// Problem: E. Permutation Shift
// Contest: Codeforces - Harbour.Space Scholarship Contest 2021-2022 (open for everyone, rated, Div. 1 + Div. 2)
// URL: https://codeforces.com/contest/1553/problem/E
// Memory Limit: 256 MB
// Time Limit: 3000 ms
// Data:2021-08-10 13:46:54

#include <bits/stdc++.h>
#define debug(a, b) printf("%s = %d\\n", a, b);
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll= 1e18;
const int INF_int= 0x3f3f3f3f;
inline ll read()
{
    ll s= 0, w= 1ll;
    char ch= getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            w= -1ll;
        ch= getchar();
    }
    while (ch >= '0' && ch <= '9')
        s= s * 10ll + ((ch - '0') * 1ll), ch= getchar(); //s=(s<<3)+(s<<1)+(ch^48);
    return s * w;
}
void rd_test()
{
#ifdef ONLINE_JUDGE
#else
    startTime= clock();
    freopen("in.txt", "r", stdin);
#endif
}
void Time_test()
{
#ifdef ONLINE_JUDGE
#else
    endTime= clock();
    printf("\\nRun Time:%lfs\\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
#endif
}
const int maxn= 3e5 + 9;
int a[maxn];
int p[maxn], num[maxn], n, m;
bool vis[maxn];
void init()
{
    for (int i= 1; i <= n; i++)
        num[i]= 0;
}
bool check(int k)
{
    for (int i= 1; i <= n; i++)
        vis[i]= 0;
    int ant= 0;
    //将移动k位置后的情况记录
    for (int i= k + 1; i <= n; i++) {
        p[++ant]= a[i];
    }
    for (int i= 1; i <= k; i++) {
        p[++ant]= a[i];
    }
    //置换群
    int crinum= n;
    for (int i= 1; i <= n; i++) {
        if (vis[i])
            continue;
        crinum--;
        int pos= i;
        while (!vis[pos]) {
            vis[pos]= 1;
            pos= p[pos];
        }
    }
    return crinum <= m;
}
int main()
{
    //rd_test();
    int t= read();
    while (t--) {
        init();
        n= read(), m= read();
        for (int i= 1; i <= n; i++) {
            a[i]= read();
            num[(i - a[i] + n) % n]++;
        }
        vector<int> vec;
        for (int i= 0; i < n; i++) //偏移量
        {
            if (num[i] >= n - 2 * m && check(i)) {
                vec.push_back(i);
                //if (vec.size() >= 3)
                //    break;
            }
        }
        printf("%d", vec.size());
        for (auto i : vec) {
            printf(" %d", i);
        }
        printf("\\n");
    }

    //Time_test();
}

以上是关于cf1553E. Permutation Shift的主要内容,如果未能解决你的问题,请参考以下文章

Square Root of Permutation - CF612E

CF1295E Permutation Separation

CF1295E Permutation Separation

CF1156E Special Segments of Permutation题解瞎搞 单调栈

cf1553C. Penalty

cf1553D. Backspace