CF1973E Cat, Fox and Swaps 题解
题意:对于一个长度为 \(n\) 的排列,求有多少对 \((l,r)\) 满足 \(1 \le l,r \le 2n\),且可以通过交换任意次 \(x,y(l \le x+y \le r)\) 使得原排列升序。
首先我们可以找到 \(i \ne a_i\) 的最小和最大的 \(i\),假设为 \(L\) 和 \(R\)。若不存在则说明已经升序。
会发现满足条件的必要条件是:\(l \le L + n,r \ge R + 1\)。
神奇的是若除开 \(l=r\) 的情况,上述条件也是充分条件,证明如下:
对于一个数 \(L \le x \le R-1\),我们先找到一个 \(y\) 满足 \(l \le y < r\) 且 \(1 \le y-x \le n\),这个 \(y\) 一定存在的。那么我们可以通过交换 \((x,y-x),(y-x,x+1),(x,y-x)\) 达到交换 \((x,x+1)\) 的目的。即对于任意的 \(L \le x \le R\),我们都能做到交换 \((x,x+1)\)(并且其它数的位置不变),显然这一定能使原序列排序。
注意当 \(l=r\) 的情况时上述条件不一定成立,需要特殊考虑一下。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 1e5 + 10;
int n,T,a[MAXN];
signed main() {
for(cin >> T;T;T--) {
cin >> n;
int l = -1,r = -1,ans = 0,flag = 0;
for(int i = 1;i <= n;i++) cin >> a[i];
for(int i = 1;i <= n;i++)
if(a[i] != i) {
if(flag == -1) break;
if(flag == 0) flag = a[i] + i;
else if(flag != a[i] + i) flag = -1;
}
for(int i = 1;i <= n;i++) if(a[i] != i) r = i;
for(int i = n;i >= 1;i--) if(a[i] != i) l = i;
if(l == -1) l = 2 * n,r = 1;
else l = l + n,r = r + 1;
for(int i = r;i <= 2 * n;i++)
ans += min(i - 1,l);
if(flag == 0) ans += 2 * n;
if(flag > 0) ans += 1;
cout << ans << endl;
}
return 0;
}