题目大意
给定一个 n n n个数的数组 a a a。
每次可以选择三个数 a i , a j , a k ( 1 ≤ i , j , k ≤ n ) a_i, a_j, a_k(1 \le i,j,k \le n) ai,aj,ak(1≤i,j,k≤n),使得 a i a_i ai到 a j a_j aj原来的位置, a j a_j aj到 a k a_k ak原来的位置, a k a_k ak到 a i a_i ai原来的位置。( 1 ≤ n , a i ≤ 1 0 5 1 \le n, a_i \le 10^5 1≤n,ai≤105)
问是否可以通过这种方式使得数组变成升序有序数组
思路
首先需要知道这么一个规律,交换一个序列中两个不同的数会使这个序列的逆序对数改变奇数个。
而题目的这种操作就像与连续进行两次操作(交换 a i , a j a_i, a_j ai,aj,然后交换 a j , a k a_j ,a_k aj,ak),那么显然逆序数的变换是偶数个。
所以,只要原序列的逆序数是偶数,那么肯定可以做到。
当然还有一种特殊情况:
有两个相同的数,那么我们可以把这两个数作为媒介,可以等价于随意交换任意两个数,所以此时无论如何都能交换成功。
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxN = 5e5 + 7;
int T, n, a[maxN], b[maxN], c[maxN];
bool cmp(int x, int y)
{
return x < y;
}
inline void Add(int x, int k)
{
for(; x <= n; x += x&(-x))
c[x] += k;
}
inline int GetSum(int x)
{
int ans = 0;
for(; x; x -= x&(-x))
ans += c[x];
return ans;
}
int main()
{
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
b[i] = a[i];
}
sort(a + 1, a + 1 + n, cmp);
int cnt = 0; bool mul = false;
for(int i = 1; i <= n; ++i)
if(a[i] == a[i - 1]) { //判断是否有重复
mul = true;
break;
}
if(mul)
printf("YES\n");
else {
int num = 0;
memset(c, 0, sizeof c);
for(int i = n; i > 0; i--) {
num += GetSum(b[i] - 1);
Add(b[i], 1);
}
if(num & 1)
printf("NO\n");
else
printf("YES\n");
}
}
return 0;
}