【补题】Codeforces Global Round 20 F1. Array Shuffling
声明:本学习笔记参考来源CF1672F1 Array Shuffling - 洛谷的题解,里面有这题的详细证明,有更优秀的代码,本篇甚至有点偷工减料,仅仅是多了本人自己的理解。
题意:给定序列A,问你能不能给出序列B,使得小O在执行使任意两个位置的数字随意交换的操作,让B变成A操作次数最多的序列B。
思路: 本篇没有证明,只是个人模拟赛时的思考
1.首先思考到假设所有的数字不相同,其实你会想到一个很简单的构造方式,那就是所有的数字统一向某一个方向移动一次。一定会导致所有的数字错开,并且一定要一遍一遍操作。
其实就是让每一个数字都不在原本的位置上,且不会出现交换后直接安置好了两个位置
2.数字相同的时候似乎出现了问题,因为左移一位使的还有数字可能停留在大小一样的数字上面,根据上面的结论,考虑安排几个数字在不相同的情况下,不处在自己原本位置即可。
那也就是安排一些数字形成环,然后进行位移,但是这会轻易的WA,问题在于简单的移动环,可能会导致环之间产生反应,出现了交换后安置在正确的位置。
3.考虑一种构造模式,按照数字的大小排序后,利用数字的大小进行位移,而不是下标。这样每一个数字一定不会在正确的位置的同时,两个环就算出现反应也是同效,因此构造成功。
环就是按照那个数字曾经的出现次数安置在相同环中即可
代码:
#include <bits/stdc++.h>
#define int long long
#define int128 __int128
#define IOS \std::ios::sync_with_stdio(0); \std::cin.tie(0); \std::cout.tie(0);
const int N = 2e5 + 10;
const int INF = 1e18;
const int MOD = 998244353;int cnt[N];
std::vector<int> buk[N];
int ve[N];int cmp(const int &a, const int &b)
{return ve[a] > ve[b];
}void solve()
{int n;std::cin >> n;for (int i = 1; i <= n; i++){cnt[i] = 0;buk[i].clear();}for (int i = 0; i < n; i++){std::cin >> ve[i];++cnt[ve[i]];buk[cnt[ve[i]]].push_back(i);}for (int i = 1; i <= n; i++){std::sort(buk[i].begin(), buk[i].end(), cmp);for (int j = 1; j < buk[i].size(); j++){std::swap(ve[buk[i][j - 1]], ve[buk[i][j]]);}}for (int i = 0; i < n; i++){std::cout << ve[i] << " ";}std::cout << '\n';
}signed main()
{IOS;int t = 1;std::cin >> t;while (t--){solve();}
}