CF1918B Minimize Inversions 题解

文章介绍了如何解决CF1918B题目的解题思路,通过观察逆序对的交换规律,提出一种方法:将其中一个数组排序,另一个数组跟随调整,以达到最小化两个序列逆序对总数的目标。作者提供了代码实现,并展示了如何利用哈希表优化解决方案。
摘要由CSDN通过智能技术生成

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 n2e5

题目描述

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 1t20000 ) — 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 1n2105 ) — 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 1ain ) — 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 2105 .

输出格式

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 1n 的所有数,且每个数只出现一次,所以可以先直接输出 1 ∼ n 1 \sim n 1n,就相当于排完序的 a a a 数组;然后输出 v i s 1 ∼ v i s 5 vis_1 \sim vis_5 vis1vis5,表示 a a a 数组 1 ∼ 5 1 \sim 5 15 下标对应的 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!

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值