1. 程式人生 > 其它 >Codeforces Harbour.Space Scholarship Contest 2021-2022/1553E Permutation Shift

Codeforces Harbour.Space Scholarship Contest 2021-2022/1553E Permutation Shift

題目連結:https://codeforces.com/contest/1553/problem/E

題目大意:是否能在轉置\(k\)輪和交換\(m\)次的條件下將\((1,2,3,...,n)\)變成\((a_{1},a_{2},a_{3},...,a_{n})\)\(0\leq k<n,0\leq m\leq \frac{n}{3}\),轉置指元素後移\(k\)位,交換指兩個元素交換位置

題目思路:
先將每個數都減\(1\)
轉置0輪\((0,1,2,3)\)
轉置1輪\((3,0,1,2)\)
轉置2輪\((2,3,0,1)\)
轉置3輪\((1,2,3,0)\)
不難發現轉置k輪實際上就是\(p[i] = i-k(mod \ n)\)


對於每一個轉置我們可以求出對a序列來說不動點的個數
a序列為\((1,2,0,3)\)
轉置0輪\((0,1,2,3)\),不動點數\(cnt[0] = 1\)
轉置1輪\((3,0,1,2)\),不動點數\(cnt[1] = 0\)
轉置2輪\((2,3,0,1)\),不動點數\(cnt[2] = 1\)
轉置3輪\((1,2,3,0)\),不動點數\(cnt[3] = 2\)
不動點即\(a[i] =p[i] = i-k(mod \ n)\),得 \(k = i-a[i] (mod \ n)\),因\(++cnt[k]\)即可
剩下都是需要移動的點,交換\(m\)次最多可導致\(2m\)個元素交換,因此可以判斷如果\(cnt[k]+2m<n\)
當前轉置就不滿足條件,跳過即可
因為\(m\leq \frac{n}{3}\),最多可以交換\(\frac{2n}{3}\)個元素,剩下\(\frac{n}{3}\)是不動點的個數,又因為\(\sum cnt[k] = n\),最多隻有3個\(k\)是滿足條件的,直接暴力即可
暴力求出環數\(num\),需要移動的點即為\(n-num\)

AC程式碼:

#include <unordered_map>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <string>
#include <stack>
#include <deque>
#include <queue>
#include <cmath>
#include <map>
#include <set>
using namespace std;
typedef pair<int, int> PII;
typedef pair<double, int> PDI;
//typedef __int128 int128;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int N = 3e5 + 10, M = 4e7 + 10;
const int base = 1e9;
const int P = 131;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);
int a[N], cnt[N], ans[5];
int pos;
bool vis[N];
int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		pos = 0;
		int n, m;
		scanf("%d%d", &n, &m);
		for (int i = 0; i < n; ++i)
		{
			scanf("%d", &a[i]);
			--a[i];
			++cnt[(i - a[i] + n) % n];
		}
		for (int k = 0; k < n; ++k)
		{
			if (cnt[k] + 2 * m < n)
				continue;
			for (int i = 0; i < n; ++i)
				vis[i] = false;
			int num = 0;
			for (int i = 0; i < n; ++i)
			{
				if (vis[i])
					continue;
				for (int j = i; !vis[j]; j = (a[j] + k) % n)
					vis[j] = true;
				++num;
			}
			if (n - num <= m)
				ans[++pos] = k;
		}
		printf("%d ", pos);
		for (int i = 1; i <= pos; ++i)
			printf("%d ", ans[i]);
		printf("\n");
	}
	return 0;
}