置换环
1 作用
置换环是用来求解数组排序元素间所需最小交换次数这类问题。
置换环思想:置换环将每个元素指向其排序后应在的位置,最终首位相连形成一个环(若数字在最终位置,则其自身成环),可知元素之间的交换只会在同一个环内进行,而每个环内的最小交换次数为
环上的元素 - 1
排序所需最小交换次数为数组长度-环的个数
。
考虑置换环,也就是 i↔a[i] 之间连边。
例如以下排列 [1,7,5,8,2,6,3,4]
1 - >1 6 - > 6 2 - > 7 - > 3 - > 5 - > 2
我们看看那个4个点的环
在排列上,进行交换
2 3 5 7 -------------------> 2 3 5 7
7 5 2 3 -------------------> 3 5 2 7 (交换a[2],a[7])
2 3 5 7 -------------------> 2 3 5 7
3 5 2 7 -------------------> 2 5 3 7 (交换a[2],a[5])
2 3 5 7 -------------------> 2 3 5 7
2 5 3 7 -------------------> 2 3 5 7 (交换a[3],a[5])
执行了3次交换,就可以使得4个数,到 [1,2,3,4,5...n] 相应的位置上。
如果有 m 个环,那么只需要 n−m 次交换就可以把排列变成 [1,2,3...n] 了。
然后执行一次相邻交换的操作就可以了。
但是,还有一种情况。
对于上面的4个点的环,我们这样交换
2 3 5 7 -------------------> 2 3 5 7
7 5 2 3 -------------------> 3 5 2 7 (交换a[2],a[7])
2 3 5 7 -------------------> 2 3 5 7
3 5 2 7 -------------------> 3 2 5 7 (交换a[3],a[5])
此时,我们交换了两次,而 a[2],a[3] 构成了一个逆序对。
也就是,如果环中,有相邻的两个点,那么可以通过减少一次交换,使得其贡献出一个逆序对。
此时的操作次数为 n−m−1

// Problem: D. Lucky Permutation // Contest: Codeforces - Codeforces Round 842 (Div. 2) // URL: https://codeforces.com/contest/1768/problem/D // Memory Limit: 256 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; #define int long long #define sc(x) scanf("%lld",&x) #define pr(x) printf("%lld\n",x) #define YES puts("YES"); #define NO puts("NO"); #define Yes puts("Yes"); #define No puts("No"); #define fo(i,j,k) for(int i = j;i <= k;i ++) #define pre(i,j,k) for(int i = j;i >= k;i --) #define ls idx*2 #define rs idx*2+1 int pow_mod(int a1, int n1, int m1) { if(n1 == 0) return 1; int x = pow_mod(a1, n1/2, m1); long long ans = (long long)x * x % m1; if(n1 % 2 == 1) ans = ans *a1 % m1; return (int)ans; } inline __int128 read(){ __int128 x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } inline void print(__int128 x){ if(x < 0){ putchar('-'); x = -x; } if(x > 9) print(x / 10); putchar(x % 10 + '0'); } // bool operator<(NOW a, NOW b) { // if(a.site == b.site) // return a.x > b.x; // return a.site< b.site; // } const int N = 1e6 + 10; int n,m; int a[N],b[N]; bool f; map<int,int>mp; void dfs(int u) { if(b[u]) return ; b[u] = 1; mp[u] = 1; if(mp[u+1] == 1||mp[u-1]== 1) f = true; dfs(a[u]); } void solve() { cin >> n; for(int i = 1;i <= n;i ++) cin >> a[i],b[i] = 0; int num = 0; f = false; for(int i = 1;i <= n;i ++) { if(!b[i]) { num ++; mp.clear(); dfs(i); } } //cout << num << '\n'; if(f) cout << n - num - 1 << '\n'; else cout << n-num+1 << '\n'; } signed main() { int tt = 1; sc(tt); while(tt--) { solve(); } }