1. 程式人生 > 實用技巧 >CF612E Square Root of Permutation 題解 圖論建圖題

CF612E Square Root of Permutation 題解 圖論建圖題

題目連結:http://codeforces.com/contest/612/problem/E

題目大意:給你一個 \(1\)\(n\) 的排列,求一個 \(1\)\(n\) 的排列 \(q\) 滿足 \(q_{q_i} = p_i\)。無解則輸出 \(-1\)

解題思路(完全參照自 SovietPower大佬的部落格):

對排列 \(q_i\) 我們建一個圖,邊為 \(i \rightarrow q_i\)。顯然這張圖是有幾個環構成的。

對於 \(p_i\)(即 \(q_{q_i}\)),原來 \(q_i\) 中的奇環在 \(p_i\) 中也是由同樣的資料組成的奇環,只不過排列順序變成了 \(1,2,5,\ldots,n,2,4,\ldots,n-1\)

;原來的偶環會變成兩個大小相等的偶環(一個由 \(1,3,5,\ldots,n-1\) 構成,一個由 \(2,4,6\ldots,n\) 構成)。

所以對 \(p_i\) 建圖,找出對應的環。奇環單獨處理,兩個大小相同的偶環一起處理。就能還原出 \(q_i\) 對應的圖了。

示例程式碼如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000010;
int n, p[maxn];
int sz[maxn], head[maxn], tmp[maxn], m, q[maxn];
bool vis[maxn];

bool cmp(int i, int j) {
	return sz[i] < sz[j];
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i ++) scanf("%d", p+i);
	for (int i = 1; i <= n; i ++) {
		if (!vis[i]) {
			head[m++] = i;
			int j = i;
			while (!vis[j]) {
				vis[j] = true;
				sz[i] ++;
				j = p[j];
			}
			if (j != i) {
				puts("-1");
				return 0;
			}
		}
	}
	sort(head, head+m, cmp);
	for (int i = 0; i < m; i ++) {
		int u1 = head[i], u2 = head[i+1];
		if (sz[u1] % 2) {	// 奇環 
			int x = u1, y = 0;
			for (int j = 0; j < sz[u1]; j ++) {
				tmp[y] = x;
				y = (y + 2) % sz[u1];
				x = p[x];
			}
			assert(x == u1);
			for (int j = 0; j < sz[u1]; j ++)
				q[ tmp[j] ] = tmp[(j+1) % sz[u1]];
		}
		else {	// 偶環 
			if (i == m-1 || sz[u1] != sz[u2]) {
				puts("-1");
				return 0;
			}
			// 處理偶環
			int x = u1, y = u2;
			for (int i = 0; i < sz[u1]; i ++) {
				q[x] = y;
				q[y] = p[x];
				x = p[x];
				y = p[y];
			} 
			i ++;	// 多加上1 
		}
	}
	for (int i = 1; i <= n; i ++) printf("%d ", q[i]);
	return 0;
}