E-Permutation Shift

链接:https://codeforces.com/contest/1553/problem/E

题意:
对一个从1到n的序列
第一步:先进行任意次滑动,比如说1234向右滑动一次变成4123
第二步:进行任意次任选两个元素两两交换
输入结果序列,和进行的交换数(交换数小于n/3)
要求输出序列可能由原序列滑动的次数

题解:
因为交换数小于n/3,所以滑动了i个位置的元素必定大于n/3个
可知,最多的可能滑动次数只有3个
所以可以暴力判断
如果符合了滑动i次的数字大于n/3
就判断交换数是否符合
而通过公式: 最小交换次数=总点数-连通分量数
所以对于每个对应点对,连一条边,然后看成图判断连通分量数
注意序列是滑动了k次的,所以1的对应点应该是k+1,应该注意一下坐标的偏移

#include<bits/stdc++.h>
using namespace std;
void ope(){
    int n;
    int m;
    cin>>n>>m;
    vector<int> cnt(n);//偏移位置为i的数字的个数
    vector<int> num(n);//数字
    vector<int> ans;
    int ansnum=0;
    for(int i=0;i<n;i++){
        cin>>num[i];
        num[i]--;
        cnt[(i-num[i]+n)%n]++;//这里必须+n,否则取模可能是负数
    }
    for(int s=0;s<n;s++){
        if(cnt[s]<1.0*n/3)continue;
        //偏移位置相同的数字个数小于n/3时不可能是答案
        int k=0;
        vector<bool> vis(n);
        for(int i=0;i<n;i++){//判断图的连通分量数
            if(vis[i])continue;
            int t=i;
            k++;
            while(!vis[t]){//这里进入了环
                vis[t]=1;
                t=num[(t+s)%n];
            }
        }
        if(n-k<=m){//最小交换次数=总点数-连通分量数
            ans.push_back(s);
            ansnum++;
        }
    }
    cout<<ansnum<<" ";
    for(auto i:ans)cout<<i<<" ";
    cout<<endl;
}
int main(){
    int t;
    cin>>t;
    while(t--){
        ope();
    }
    //system("pause");
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值