CF1721C Min-Max Array Transformation(div.2)

题目链接

https://codeforces.com/problemset/problem/1721/C

题意简述

先给你一个非降序数组 \(a\) , 再创建两个数组 \(b\)\(d\) ,我们令 $b_i =a_i+d_i $ ,然后再对数组 \(b\) 排序,并把排序后的数组 \(b\) 给你,请你找出在所有可能的数组 \(d\) 中 , \(d_i\) 的最小值和最大值 , 注意,每一个 \(d_i\)是相互独立的,这意味着每一个 \(d_i\) 可以来自于不同的数组 \(d\).

比如现在有两个数组 \(d\)
\(2\) \(4\) \(6\)
\(1\) \(7\) \(9\)
则得到的 \(d_i\) 的最小值数组与最大值数组分别为
\(1\,\,4\,\,6\)
\(2\,\,7\,\,9\)

样例

点击查看样例

image

算法标签
二分,贪心

分析

对于 \(d_i\) 的最小值,可以直接对数组 \(b\) 二分,找最小的大于等于 \(a_i\) 的.

对于 \(d_i\) 的最大值,如果是从前往后计算:我们应该先处理\(a[i+1]\sim a[n]\),显然,只需要从\(a[i+1]\) 开始,删除数组 \(b\) 中最小的大于等于 \(a[i+1]\) 的元素(这是 \(a[j]\) 能满足 \(d[j]\) 非负这个条件的最低要求),剩下的元素中的最大值就是 \(a[i]\) 对应的 \(d[i]\) .从前往后的话每一次都要复制一遍 $ b$ 数组然后删除元素,而从后往前取完最大值后可以直接删除最小的大于等于 \(a[j]\) 的 ,所以从后往前会更方便.
如果理解了为什么要从后往前可以忽略下面这段.
应该从后往前计算,因为要优先满足大的元素的要求,否则你当前的 \(a_i\) 先把大的位置占了,后面大的值就没有对应的 \(b_i\)了.然后每一次取当前 \(b_i\) 的最大值,并删掉最小的大于等于 \(a_i\) 的元素(这是 \(a_i\) 能有解的最低要求,不要直接删除 \(b_i\) 中最大的元素,因为前面的元素可能还能用到 )

multiset 是可以有重复元素的集合,刚开始不知道,就用了 map ,导致代码写的很长

一部分hack样例

\(Sample\) \(Input\)

1
3
10 20 30 30
10 22 40 50

\(Sample\) \(Output\)

0 2  10 10
0 12 20 20

代码

点击查看代码
#include<stdio.h>
#include<iostream>
#include<cstdlib>
#include<string.h>
#include<algorithm>
#include<set>
using namespace std;
const int N=2e5+10;
int a[N];
int b[N];
int d[N];
multiset<int> s;
int main()
{
	//freopen("uva.txt","r",stdin);
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)scanf("%d",&a[i]);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&b[i]);
			s.insert(b[i]);//把b数组的元素存入multiset中,可以快速地查找和删除元素.时间复杂度O(logn)
		}
		for(int i=1;i<=n;i++)
		{
			int pos=lower_bound(b+1,b+n+1,a[i])-b;
			if(pos<=n)
			{
				d[i]=b[pos]-a[i];
			}
		}
		for(int i=1;i<=n;i++)
		{
			printf("%d ",d[i]);
		}
		printf("\n");
		for(int i=n;i>=1;i--)
		{
			d[i]=*(--s.end())-a[i];// *(--s.end())意为取b中的未被删除的最大的元素
			
			//auto t=lower_bound(s.begin(),s.end(),a[i]);
			//s.erase(t);//删掉最小的大于等于a[i]的元素
			/*按上面的写法交上去TLE了,下面的写法AC.经询问大佬得知,lower_bound在用于set,map这种(底层是红黑树实现的)结构时复杂度是O(n),所以最好用他自带的,复杂度为O(logn)*/
			
			s.erase(s.lower_bound(a[i]));
			
		}
		for(int i=1;i<=n;i++)
		{
			printf("%d ",d[i]);
		}	
		printf("\n");
	}
	
	return 0;
}

补充

image

posted @ 2022-09-16 13:41  LZH_03  阅读(33)  评论(0编辑  收藏  举报