题目链接:
https://codeforces.com/problemset/problem/1591/D
题目大意:
给定一个长度为 \(n\) 的序列,可以选择其中的一个三元组 \((i, j, k)\),按顺序移动 \(i -> j -> k -> i\),可以进行任意次该操作,判断是否能使该序列变成非递减序列。
思路:
从序列逆序对改变的性质去考虑。
三个元素顺序移动之后,
若三个元素都不相同,那么移动后序列的逆序对的数量改变偶数个,+2 或者 -2。而非递减的序列的逆序对的数量是 0,所以我们只需要计算当前序列的逆序对的数量是否为偶数个就可以了,偶数个的序列的逆序对经变化一定可以变为 0。
若有两个元素相同,那么序列的逆序对的数量可能变化奇数个,也可能变化偶数个,所以不管原序列的逆序对数量是奇数还是偶数,一定可以形成一个非递减的序列。
通过归并法计算逆序对:
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int T, n, a[N], tmp[N], sum;
void mergeSort(int l, int r){
if (l >= r) return;
int mid = (l + r) >> 1, i = l, j = mid + 1, cnt = 0;
mergeSort(l, mid);
mergeSort(mid + 1, r);
while (i <= mid || j <= r)
if (j > r || (i <= mid && a[i] <= a[j]))
tmp[cnt++] = a[i++];
else
tmp[cnt++] = a[j++], sum += mid - i + 1;
for (int k = 0; k < r - l + 1; k++)
a[l + k] = tmp[k];
}
void solve(){
bool f = false;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
sum = 0;
mergeSort(1, n);
if (sum % 2 == 0) cout << "YES\n";
else{
for (int i = 2; i <= n; i++)
if (a[i] == a[i - 1]){
cout << "YES\n";
return;
}
cout << "NO\n";
}
}
int main(){
cin >> T;
while (T--)
solve();
return 0;
}
树状数组计算逆序对
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 5e5 + 10;
LL ans;
int T, n, tree[N], p[N];
struct node{
int num, index;
}nd[N];
bool cmp(node a, node b){
if (a.num == b.num) return a.index < b.index;
return a.num < b.num;
}
int lowbit(int k){
return k & -k;
}
void update(int x, int k){
while (x <= n){
tree[x] += k;
x += lowbit(x);
}
}
int query(int x){
int t = 0;
while (x != 0){
t += tree[x];
x -= lowbit(x);
}
return t;
}
void solve(){
scanf("%d", &n);
ans = 0;
for (int i = 1; i <= n; i++){
scanf("%d", &nd[i].num);
nd[i].index = i;
tree[i] = 0; //初始化,慎用 memset
}
sort(nd + 1, nd + n + 1, cmp);
for (int i = 2; i <= n; i++)
if (nd[i].num == nd[i - 1].num){
cout << "YES\n";
return;
}
for (int i = 1; i <= n; i++)
p[nd[i].index] = i;
for (int i = 1; i <= n; i++){
update(p[i], 1);
ans += (i - query(p[i]));
}
if (ans % 2 == 0) cout << "YES\n";
else cout << "NO\n";
}
int main(){
cin >> T;
while (T--)
solve();
return 0;
}
排行榜上一位神犇的做法
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
#define LL long long
LL t, n;
void solve(){
bool f = false;
scanf("%lld", &n);
vector <int> a(n), cnt(n);
for (int i = 0; i < n; i++){
scanf("%d", &a[i]);
cnt[--a[i]]++;
if (cnt[a[i]] > 1) f = true;
}
if (f) cout << "YES\n";
else {
int sum = 0;
vector<bool> vis(n);
for (int i = 0; i < n; i++)
if (!vis[i]) {
int u = i, k = 0;
while (!vis[u]) {
k++;
vis[u] = true;
u = a[u];
}
sum += k - 1;
}
cout << (sum % 2 == 0 ? "YES" : "NO") << "\n";
}
}
int main(){
cin >> t;
while (t--)
solve();
return 0;
}