链接: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;
}