Harbour.Space Scholarship Contest 2021-2022 (open for everyone, rated, Div. 1 + Div. 2)

E. Permutation Shift

本题的题意是:你有种操作是让排列(原排列为1,2,3....n)循环,你循环k次则原来的排列变为a[(i-k+n)%n],例如k=2,就变成了n-1,n,1,2,3,....n-2。现在问你,
给你一个排列b[n],让你找出从(1,2,3,...n)有多少种k,以及循环k次后最多可以任意交换两个数m次,使得这个排列变为b[n]
首先我们先找出b[n]中每一个数对应的循环多少次vis[(i-a[i]+m)%n],然后枚举每一种k的可能,因为m最多是n/3,所以最多枚举出3个不同的k,
(如果一个k是满足条件的,那么至少vis[i]得大于n/3,不然的话就算m等于n/3那么也不可能使得全部数到该到的位置上),然后是判断这个k是否合法,这有一个定理,
一个排列变成另一个排列是他们对应的(a[i],b[i])连边形成的联通块的个数cnt,然后需要的交换次数就是n-cnt;
#include <algorithm>
#include <deque>
#include <iomanip>
#include <iostream>
#include <map>
#include <math.h>
#include <queue>
#include <set>
#include <stack>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <unordered_map>
#include <vector>
#define ll long long int
#define ms(a, b) memset(a, b, sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define se second
#define ull unsigned long long
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define endl "\n"
#define bug printf("-----------bug---------");
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
using namespace std;
const int maxn = 3e5 + 10;
const int maxm = 6e2 + 50;
const double eps = 1e-8;
const ll inf = 0x3f3f3f3f;
const ll lnf = 0x3f3f3f3f3f3f3f3f;
const double pi = acos(-1);
const ll mod = 1e9 + 7;

int n, m;
int vis[maxn];
int a[maxn], b[maxn];
vector<int> v;
int fa[maxn];
int cnt;
int find1(int x)
{
    return fa[x] == x ? x : fa[x] = find1(fa[x]);
}
bool  chekc(int x)
{
    for (int i = 1; i <= n;i++)
    {
        fa[i] = i;
    }
    for (int i = 1; i <= n;i++)
    {
        b[i] = (i - 1 - x + n) % n + 1;
    }
    int cnt = 0;
    for (int i = 1; i <= n;i++)
    {
        int dx = find1(a[i]);
        int dy = find1(b[i]);
        if(dx==dy)
        {
            cnt++;
            continue;
        }
        fa[a[i]] = b[i];
    }
    return n - cnt <= m;
}
void sove()
{
    v.clear();
    cin >> n >> m;
    for (int i = 1; i <= n;i++)
    {
        vis[i] = 0;
    }
    for (int i = 1; i <= n;i++)
    {
        cin >> a[i];
        vis[(i - a[i] + n) % n]++;
    }
    for (int i = 0; i < n;i++)
    {
        if(vis[i]<n/3)
            continue;
        else
        {
            if(chekc(i))
            {
                v.push_back(i);
            }
        }
    }
    cout << (int)v.size();
    for(auto u:v)
    {
        cout << " " << u;
    }
    cout << endl;
}
int main()
{
    IOS;
    int T;
    cin >> T;
    while(T--)
    {
        sove();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值