【思维构造】Neutral Tonality—CF1894D

Neutral Tonality—CF1894D

翻译

给定一个由 n n n个整数组成的数组 a a a,以及一个由 m m m个整数组成的数组 b b b

LIS ( c ) \text{LIS}(c) LIS(c)表示数组 c c c的最长递增子序列的长度。例如, LIS ( [ 2 , 1 ‾ , 1 , 3 ‾ ] ) \text{LIS}([2, \underline{1}, 1, \underline{3}]) LIS([2,1,1,3]) = 2 2 2 LIS ( [ 1 ‾ , 7 ‾ , 9 ‾ ] ) \text{LIS}([\underline{1}, \underline{7}, \underline{9}]) LIS([1,7,9]) = 3 3 3 LIS ( [ 3 , 1 ‾ , 2 ‾ , 4 ‾ ] ) \text{LIS}([3, \underline{1}, \underline{2}, \underline{4}]) LIS([3,1,2,4]) = 3 3 3

你需要将数字 b 1 , b 2 , … , b m b_1, b_2, \ldots, b_m b1,b2,,bm插入到数组 a a a中的任意位置,任意顺序。令结果数组为 c 1 , c 2 , … , c n + m c_1, c_2, \ldots, c_{n+m} c1,c2,,cn+m。你需要选择插入位置以最小化 LIS ( c ) \text{LIS}(c) LIS(c)

也就是说,你需要找到一个数组 c 1 , c 2 , … , c n + m c_1, c_2, \ldots, c_{n+m} c1,c2,,cn+m,同时满足以下条件:

  • 数组 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an是数组 c 1 , c 2 , … , c n + m c_1, c_2, \ldots, c_{n+m} c1,c2,,cn+m的子序列。
  • 数组 c 1 , c 2 , … , c n + m c_1, c_2, \ldots, c_{n+m} c1,c2,,cn+m由数字 a 1 , a 2 , … , a n , b 1 , b 2 , … , b m a_1, a_2, \ldots, a_n, b_1, b_2, \ldots, b_m a1,a2,,an,b1,b2,,bm组成,可能重新排列。
  • LIS ( c ) \text{LIS}(c) LIS(c)的值是所有合适数组 c c c最小可能的值。

输入

每个测试包含多个测试用例。第一行包含一个整数 t t t ( 1 ≤ t ≤ 1 0 4 ) (1 \leq t \leq 10^4) (1t104),表示测试用例的数量。接下来是测试用例的描述。

每个测试用例的第一行包含两个整数 n , m n, m n,m ( 1 ≤ n ≤ 2 ⋅ 1 0 5 , 1 ≤ m ≤ 2 ⋅ 1 0 5 ) (1 \leq n \leq 2 \cdot 10^5, 1 \leq m \leq 2 \cdot 10^5) (1n2105,1m2105),表示数组 a a a和数组 b b b的长度。

每个测试用例的第二行包含 n n n个整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an ( 1 ≤ a i ≤ 1 0 9 ) (1 \leq a_i \leq 10^9) (1ai109),表示数组 a a a的元素。

每个测试用例的第三行包含 m m m个整数 b 1 , b 2 , … , b m b_1, b_2, \ldots, b_m b1,b2,,bm ( 1 ≤ b i ≤ 1 0 9 ) (1 \leq b_i \leq 10^9) (1bi109),表示数组 b b b的元素。

保证所有测试用例中 n n n的总和不超过 2 ⋅ 1 0 5 2\cdot 10^5 2105 m m m的总和不超过 2 ⋅ 1 0 5 2\cdot 10^5 2105

输出

对于每个测试用例,输出 n + m n + m n+m个数字,即插入后获得的最终数组 c 1 , c 2 , … , c n + m c_1, c_2, \ldots, c_{n+m} c1,c2,,cn+m的元素,使得 LIS ( c ) \text{LIS}(c) LIS(c)的值最小化。如果有多个答案,可以输出其中任意一个。

注意

第一个测试用例中, LIS ( a ) = LIS ( [ 6 , 4 ] ) = 1 \text{LIS}(a) = \text{LIS}([6, 4]) = 1 LIS(a)=LIS([6,4])=1。我们可以在 6 6 6 4 4 4之间插入数字 5 5 5,然后 LIS ( c ) = LIS ( [ 6 , 5 , 4 ] ) = 1 \text{LIS}(c) = \text{LIS}([6, 5, 4]) = 1 LIS(c)=LIS([6,5,4])=1

第二个测试用例中, LIS ( [ 1 ‾ , 7 , 2 ‾ , 4 ‾ , 5 ‾ ] ) \text{LIS}([\underline{1}, 7, \underline{2}, \underline{4}, \underline{5}]) LIS([1,7,2,4,5]) = 4 4 4。插入后, c = [ 1 , 1 , 7 , 7 , 2 , 2 , 4 , 4 , 5 , 5 ] c = [1, 1, 7, 7, 2, 2, 4, 4, 5, 5] c=[1,1,7,7,2,2,4,4,5,5]。很容易看出 LIS ( c ) = 4 \text{LIS}(c) = 4 LIS(c)=4。可以证明无法使 LIS ( c ) \text{LIS}(c) LIS(c)小于 4 4 4

思路

为了使插入的 b b b 数组对 LIS \text{LIS} LIS 的贡献最小,我们很容易想到插入的 b b b 数组中各个元素的大小应该是降序的。

这种情况下,对于 a a a 中的每个上升子序列, b b b 中最多只会有一个元素对其做出了贡献(假设有两个做出了贡献,那么这两个构成降序序列,破坏了整体的升序性质)。

那么就不难得到: LIS ( a ) ≤ LIS ( c ) ≤ LIS ( a ) + 1 \text{LIS}(a) \le \text{LIS}(c) \le \text{LIS}(a)+1 LIS(a)LIS(c)LIS(a)+1

现在我们考虑能不能找到一种插入方法,使得 LIS ( a ) = LIS ( c ) \text{LIS}(a) = \text{LIS}(c) LIS(a)=LIS(c) 恒成立。

答案是有的。那就是把 c c c 数组分成若干组,使得每一组中只有最多只有一个元素出现在某个上升子序列中。上边这句话有没有一种熟悉的感觉?是不是和上边第二段的让 b b b 数组降序的想法很相似?所以这里我们让 c c c 中的每一组内部都成降序分布。

具体来说,我们降序排序好 b b b 后,顺序遍历 a a a 数组,把当前 b b b 中剩余的满足 b j ≥ a i b_j \ge a_i bjai 的元素加到 a i a_i ai 左边( a i − 1 a_{i-1} ai1 右边)。配合代码更好理解。

C o d e Code Code

#include 
#define int long long
#define sz(a) ((int)a.size())
#define all(a) a.begin(), a.end()
using namespace std;
using PII = pair<int, int>;
using i128 = __int128;
const int N = 2e5 + 10;

int n, m;
int a[N], b[N];

void solve(int Case) {
	cin >> n >> m;
	for (int i = 1; i <= n; i ++) cin >> a[i];
	for (int i = 1; i <= m; i ++) cin >> b[i];
	
	sort(b + 1, b + m + 1, greater<int>());
	
	int j = 1;
	for (int i = 1; i <= n; i ++) {
		while (j <= m && b[j] >= a[i]) {
			cout << b[j ++] << " ";
		}
		cout << a[i] << " ";
	}
	while (j <= m) {;
		cout << b[j ++] << " ";
	}
	cout << "\n";
}

signed main() {
	cin.tie(0)->ios::sync_with_stdio(false);
	int T = 1;
	cin >> T; cin.get();
	int Case = 0;
	while (++ Case <= T) solve(Case);
	return 0;
}

你可能感兴趣的:(思维构造,c++)