• 【思维构造】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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
  • 相关阅读:
    常见的动态内存错误总结
    论文精读之 Google_v3,以及其相对于 Google_v1 和 Google_v2_BN 的模型比较
    【leetcode】【剑指offer Ⅱ】058. 日程表
    【JAVA UI】HarmonyOS怎么判断Service怎么在后台运行
    vivado 高级编程功能1
    Error
    linux常用命令-文件增删查改
    【Transformer系列】深入浅出理解Tokenization分词技术
    Hive 查看和修改 tez 容器的资源
    基于Java的流浪动物救助及领养管理设计与实现
  • 原文地址:https://blog.csdn.net/suoper2656/article/details/134296388