cf1553E. Permutation Shift
Posted Jozky86
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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