CF1918B Minimize Inversions 题解
文章目录
PART0. 题目描述
题面翻译
给定两个长度均为 n n n 的序列 A , B A,B A,B 。你可以进行任意次如下操作:
- 选择两个正整数 i , j ∈ [ 1 , n ] i,j \in [1,n] i,j∈[1,n] ,交换 A i , A j A_i,A_j Ai,Aj ,再交换 B i , B j B_i,B_j Bi,Bj 。
请最小化两个序列中逆序对的个数的和。请输出经过操作后的序列。
多测, ∑ n ≤ 2 e 5 \sum n\le 2e5 ∑n≤2e5。
题目描述
You are given two permutations a a a and b b b of length n n n . A permutation is an array of n n n elements from 1 1 1 to n n n where all elements are distinct. For example, an array [ 2 , 1 , 3 2,1,3 2,1,3 ] is a permutation, but [ 0 , 1 0,1 0,1 ] and [ 1 , 3 , 1 1,3,1 1,3,1 ] aren’t.
You can (as many times as you want) choose two indices i i i and j j j , then swap a i a_i ai with a j a_j aj and b i b_i bi with b j b_j bj simultaneously.
You hate inversions, so you want to minimize the total number of inversions in both permutations.
An inversion in a permutation p p p is a pair of indices ( i , j ) (i, j) (i,j) such that i < j i < j i<j and p i > p j p_i > p_j pi>pj . For example, if p = [ 3 , 1 , 4 , 2 , 5 ] p=[3,1,4,2,5] p=[3,1,4,2,5] then there are 3 3 3 inversions in it (the pairs of indices are ( 1 , 2 ) (1,2) (1,2) , ( 1 , 4 ) (1,4) (1,4) and ( 3 , 4 ) (3,4) (3,4) ).
输入格式
The first line contains an integer t t t ( 1 ≤ t ≤ 20 000 1 \leq t \leq 20\,000 1≤t≤20000 ) — the number of test cases.
Each test case consists of three lines. The first line contains an integer n n n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 1 \leq n \leq 2\cdot10^5 1≤n≤2⋅105 ) — the length of the permutations a a a and b b b . The second line contains n n n integers a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,…,an ( 1 ≤ a i ≤ n 1 \leq a_i \leq n 1≤ai≤n ) — permutation a a a . The third line contains b b b in a similar format.
It is guaranteed that the sum of n n n over all test cases does not exceed 2 ⋅ 1 0 5 2\cdot10^5 2⋅105 .
输出格式
For each test case, output two permutations a ′ a' a′ and b ′ b' b′ (in the same format as in the input) — the permutations after all operations. The total number of inversions in a ′ a' a′ and b ′ b' b′ should be the minimum possible among all pairs of permutations that can be obtained using operations from the statement.
If there are multiple solutions, print any of them.
样例 #1
样例输入 #1
3
5
1 2 3 4 5
5 4 3 2 1
3
3 1 2
3 1 2
6
2 5 6 1 3 4
1 5 3 6 2 4
样例输出 #1
3 2 5 1 4
3 4 1 5 2
1 2 3
1 2 3
2 3 4 6 5 1
1 2 4 3 5 6
提示
In the first test case, the minimum possible number of inversions is 10 10 10 .
In the second test case, we can sort both permutations at the same time. For this, the following operations can be done:
- Swap the elements in the positions 1 1 1 and 3 3 3 in both permutations. After the operation, a = [ 2 , 1 , 3 ] a = [ 2,1,3] a=[2,1,3], b = [ 2 , 1 , 3 ] b = [ 2,1,3 ] b=[2,1,3].
- Swap the elements in the positions 1 1 1 and 2 2 2 . After the operations, a a a and b b b are sorted.
In the third test case, the minimum possible number of inversions is 7 7 7 .
PART1. 解题思路
可以这么考虑,如果两个数组中都有逆序对,那么找两个数组同时是逆序对的位置交换一定最优。
但出题人不会造这样良心的数据,当两个数列中的逆序对数量交叉时,上述方法就不行了。
找找规律,例如 A = [ 1 , 2 ] , B = [ 2 , 1 ] A=[1,2],B=[2,1] A=[1,2],B=[2,1],这样不管怎么交换都是无解的,逆序对个数永远都是 1 1 1。
再看一个, A = [ 2 , 1 ] , B = [ 2 , 1 ] A=[2,1],B=[2,1] A=[2,1],B=[2,1],这时可以交换位置 1 1 1、 2 2 2 的元素,使逆序对数量从 2 2 2 个变为 0 0 0 个。
列举了上面两个例子,我们可以发现,只有同时满足 a i > a j a_i > a_j ai>aj 且 b i > b j b_i>b_j bi>bj 时交换逆序对数量才会减少,否则不会有变化。
简化一点,就是把其中一个数组排好序,另一个数组跟着一块移动,但不保证有序。这样一个数组逆序对数量为 0 0 0,另一个不定,是最优的情况。
PART2. 代码实现
sort 排序可以过,但是可以用一种更简洁的映射(打表)法,配合哈希。
重点: 设 v i s i vis_i visi 表示数组 a a a 中值为 i i i 的变量对应的 b b b 数组中的数为 v i s i vis_i visi。
因为题目规定 a , b a,b a,b 数组都是包含 1 ∼ n 1 \sim n 1∼n 的所有数,且每个数只出现一次,所以可以先直接输出 1 ∼ n 1 \sim n 1∼n,就相当于排完序的 a a a 数组;然后输出 v i s 1 ∼ v i s 5 vis_1 \sim vis_5 vis1∼vis5,表示 a a a 数组 1 ∼ 5 1 \sim 5 1∼5 下标对应的 b b b 数组元素即可。
复杂度 O ( T n ) O(Tn) O(Tn),应该是最好做法了。
PART3. AC 代码
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;
const int N = 2e5 + 10;
int n, m;
vector <int> a, b;
unordered_map <int, int> vis;
int main() {
int T; cin >> T;
while(T --){
cin >> n; a.resize(n + 1), b.resize(n + 1); // vector下标从0开始,这里是从1开始,所以要开n+1
for(int i = 1; i <= n; i ++){
cin >> a[i];
}
for(int i = 1; i <= n; i ++){
cin >> b[i];
vis[a[i]] = b[i];
}
for(int i = 1; i <= n; i ++){
cout << i << " ";
}
cout << endl;
for(int i = 1; i <= n; i ++){
cout << vis[i] << " ";
}
cout << "\n";
// 这里不清空a、b、vis是因为它们的值都会在下一轮输入时被覆盖,自然也就没有清空的必要了
}
return 0;
}
End
这里是 YLCHUP,谢谢大家!祝大家 AC!