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
#elsestartTime= clock();freopen("in.txt", "r", stdin);
#endif
}
void Time_test()
{
#ifdef ONLINE_JUDGE
#elseendTime= 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();
}