題目鏈接: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;
}