题解:CF1893B Neutral Tonality
CF1893B 题解
题面
题意
给定两个数组 \(a,b\),构造一个序列 \(c\),使得其包含 \(a,b\) 中的所有元素,保证 \(a\) 在其中的相对顺序不变,且最长(严格)上升子序列(\(\text{LIS}(c)\))最短。
转化一下,也可以理解为,给定两个数组 \(a,b\),要求把 \(b\) 中的所有元素按照任意顺序插入 \(a\) 中,要求最终形成的数组的最长(严格)上升子序列最短。
思路
首先因为 \(a\) 在 \(c\) 中的相对位置不变,所以必然有 \(\text{LIS}(c)\geqslant\text{LIS}(a)\)。
那么就考虑如何插入 \(b\) 可以使得 \(\text{LIS}(c)=\text{LIS}(a)\),容易想到一种简单的构造方法,现将 \(b\) 从大到小排序,对于一个数 \(b[j]\),将它插入到第一个满足 \(a[i]\leqslant b[j]\) 的 \(a[i]\) 前面,那为什么这样是 \(\text{LIS}(c)=\text{LIS}(a)\) 的呢?接下来开始证明。
首先,因为是插入到第一个满足 \(a[i]\leqslant b[j]\) 的 \(a[i]\) 前面,所以在 \(a[i]\) 前面的所有数都小于 \(b[j]\),但是因为 \(a[i]\leqslant b[j]\),所以对于一个上升子序列,选 \(a[i]\) 肯定更优(至少是一样),要是有多个数插在同一个位置也不会影响答案,因为 \(b\) 已经被从大到小排序过,所以每次的插入不会改变序列的最长(严格)上升子序列。
那么,开始打代码吧~
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
const int MN=200005;
ll T,n,m,a[MN],b[MN];
bool cmp(ll x, ll y){return x>y;}
void write(ll n){if(n>9)write(n/10);putchar(n%10+'0');}
ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
int main(){
T=read();while(T--){
n=read();m=read();for(int i=1; i<=n; i++) a[i]=read();for(int i=1; i<=m; i++) b[i]=read();
sort(b+1,b+1+m,cmp);//从大到小排序
for(int i=1,p1=1,p2=1; i<=n+m; i++){
if(p1>n||(p2<=m&&b[p2]>=a[p1])) write(b[p2++]),putchar(' ');
else write(a[p1++]),putchar(' ');
}
putchar('\n');
}
return 0;
}