【CF1553E】Permutation Shift
题目描述
An identity permutation of length \(n\) is an array \([1,2,3,\cdots,n]\).
We performed the following operations to an identity permutation of length \(n\):
firstly, we cyclically shifted it to the right by \(k\) positions, where \(k\) is unknown to you (the only thing you know is that \(0\le k\le n−1\)). When an array is cyclically shifted to the right by \(k\) positions, the resulting array is formed by taking \(k\) last elements of the original array (without changing their relative order), and then appending \(n−k\) first elements to the right of them (without changing relative order of the first \(n−k\) elements as well). For example, if we cyclically shift the identity permutation of length \(6\) by \(2\) positions, we get the array \([5,6,1,2,3,4]\);
secondly, we performed the following operation at most m times: pick any two elements of the array and swap them.
You are given the values of \(n\) and \(m\), and the resulting array. Your task is to find all possible values of \(k\) in the cyclic shift operation.
题目大意
将长度为 \(n\) 的数组 \([1,2,3,\cdots,n]\) 的前 \(k\) 位与后 \(n-k\) 位整体交换位置,并且再交换不超过 \(m(m\le \frac{n}{3})\) 个位置,给出最终的数组,问有多少个可能的 \(k\).
思路
位置不影响交换,所以我们枚举 \(k\) 将后 \(k\) 位换回来并且判断是否可以在 \(m\) 次交换内还原成 \([1,2,\cdots,n]\).
首先有一个结论,满足条件的 \(k\) 不超过 \(3\) 个. 我们令 \(cnt_k\) 为向后位移 \(k\) 后的序列有多少个不需要交换的位置,即 \(a_i=i\) 的 \(i\) 的个数,每次最多交换两个数,所以如果需要交换的数的个数超过 \(2m\) 那一定不合法,即 \(cnt_k \ge n-2m\ge n-\frac{n}{3}\times 2=\frac{n}{3}\). 而且有 \(\Sigma cnt_k=n\),所以合法的 \(k\) 一定不超过 \(3\) 个.
所以每次暴力 \(O(n)\) 并查集计算交换关系的环的个数,判断交换次数是否小于 \(m\).
#include <cstring>
#include <vector>
#include <cstdio>
using namespace std;
const int maxn = 3e5 + 10;
int t,n,m,p[maxn],fa[maxn],cnt[maxn];
vector<int> ans;
inline int find(int x) { return fa[x] = (fa[x] ^ x ? find(fa[x]) : x); }
inline void uni(int x,int y) { x = find(x); y = find(y); fa[x] = y; }
inline bool check(int k) {
for (int i = 1;i <= n;i++) fa[i] = i;
int tot(0),a[maxn];
for (int i = n-k+1;i <= n;i++) a[++tot] = p[i];
for (int i = 1;i <= n-k;i++) a[++tot] = p[i];
for (int i = 1;i <= n;i++) if (a[i] ^ i) uni(a[i],i);
tot = 0;
for (int i = 1;i <= n;i++) if (a[i] ^ i && fa[i] == i) tot++;
return n-cnt[k]-tot <= m;
}
int main() {
for (scanf("%d",&t);t--;puts(""),ans.clear()) {
scanf("%d%d",&n,&m);
for (int i = 1;i <= n;i++) {
scanf("%d",&p[i]);
cnt[(p[i]-i+n)%n]++;
}
if (cnt[0] >= n-m*2 && check(0)) ans.push_back(0);
for (int i = n-1;i;i--)
if (cnt[i] >= n-m*2 && check(i)) ans.push_back(n-i);
printf("%d",(int)ans.size());
for (size_t i = 0;i < ans.size();i++) printf(" %d",ans[i]);
for (int i = 1;i <= n;i++) cnt[(p[i]-i+n)%n]--;
}
return 0;
}