小苯的数组构造
时间限制:1 秒
空间限制:256M
网页链接
牛客tracker
牛客tracker & 每日一题,完成每日打卡,即可获得牛币。获得相应数量的牛币,能在【牛币兑换中心】,换取相应奖品!助力每日有题做,丰盈牛币日益多!

题目描述
大白熊给了小苯一个长度为 n n n 的数组 a a a,他希望小苯将数组 a a a 变成有序(非递减)的。具体的,小苯需要进行如下操作:
- 任选一个数组 b b b,长度也为 n n n,且元素满足: − 10 10 ≤ b i ≤ 10 10 -10^{10} \le b_i \le 10^{10} −1010≤bi≤1010。
- 对于所有 1 ≤ i ≤ n 1 \le i \le n 1≤i≤n,都执行 a i = a i + b i a_i = a_i + b_i ai=ai+bi。
大白熊希望在执行完操作后 a a a 数组满足有序,同时要最小化数组 b b b 的极差,即使得:
max ( b 1 , b 2 , … , b n ) − min ( b 1 , b 2 , … , b n ) \max(b_1, b_2, \dots, b_n) - \min(b_1, b_2, \dots, b_n) max(b1,b2,…,bn)−min(b1,b2,…,bn)
最小。
请你帮小苯找出一个合法的 b b b 数组吧。
注:如有多解输出任意即可。
输入描述
输入包含两行。
- 第一行一个正整数 n ( 1 ≤ n ≤ 2 × 10 5 ) n\ (1 \le n \le 2 \times 10^5) n (1≤n≤2×105),表示 a a a 的长度。
- 第二行 n n n 个整数 a i ( − 10 9 ≤ a i ≤ 10 9 ) a_i\ (-10^9 \le a_i \le 10^9) ai (−109≤ai≤109),表示数组 a a a 的元素。
输出描述
输出包含一行 n n n 个整数,表示构造出的 b b b 数组(有多解输出任意即可)。
如果找不到合法的 b b b 数组,请输出一个整数 − 1 -1 −1。
示例 1
输入:
2
1 2
输出:
114514 114514
说明: 可以构造 b = [ 114514 , 114514 ] b = [114514, 114514] b=[114514,114514],这样 b b b 的极差为 0 0 0,可以证明不存在比 0 0 0 更小的极差。
备注
数组的极差 = 数组中的最大值减去最小值。
数据范围与提示
- 1 ≤ n ≤ 2 × 10 5 1 \le n \le 2 \times 10^5 1≤n≤2×105
- − 10 9 ≤ a i ≤ 10 9 -10^9 \le a_i \le 10^9 −109≤ai≤109
- − 10 10 ≤ b i ≤ 10 10 -10^{10} \le b_i \le 10^{10} −1010≤bi≤1010
- 核心思路:
- 记最终序列 f i = a i + b i f_i = a_i + b_i fi=ai+bi,要求 f f f 非递减。 b b b 的极差等价于 max i ( f i − a i ) − min i ( f i − a i ) \max_i(f_i - a_i) - \min_i(f_i - a_i) maxi(fi−ai)−mini(fi−ai),要让它最小。
- 两种自然的构造可分别给出上界:
- 取 b i = p m x i − a i b_i = \mathrm{pmx}_i - a_i bi=pmxi−ai( p m x i \mathrm{pmx}_i pmxi 为前缀最大值),此时 f i = p m x i f_i = \mathrm{pmx}_i fi=pmxi 非递减, b b b 的极差为 max i ( p m x i − a i ) \max_i(\mathrm{pmx}_i - a_i) maxi(pmxi−ai);
- 取 b i = s m n i − a i b_i = \mathrm{smn}_i - a_i bi=smni−ai( s m n i \mathrm{smn}_i smni 为后缀最小值),此时 f i = s m n i f_i = \mathrm{smn}_i fi=smni 非递减, b b b 的极差为 max i ( a i − s m n i ) \max_i(a_i - \mathrm{smn}_i) maxi(ai−smni)。
- 最优极差为
D = min { max i ( p m x i − a i ) , max i ( a i − s m n i ) } D = \min\left\{ \max_i(\mathrm{pmx}_i - a_i),\ \max_i(a_i - \mathrm{smn}_i) \right\} D=min{imax(pmxi−ai), imax(ai−smni)}
选择上述两种构造中更优的一种输出即可(也可以在此基础上统一平移,使得所有 b i b_i bi 都落在 [ − 10 10 , 10 10 ] [-10^{10}, 10^{10}] [−1010,1010] 内)。
- 由于 ∣ a i ∣ ≤ 10 9 |a_i| \le 10^9 ∣ai∣≤109,构造出的 b i b_i bi 绝对值不会超过 2 × 10 9 2 \times 10^9 2×109,必然满足约束,因此答案永远不会是 − 1 -1 −1(该分支仅为格式完备保留)。
- 时间复杂度 O ( n ) O(n) O(n)。
解题思路
本题是构造 + 贪心的经典问题。给定一个长度为 n n n 的数组 a a a,需要构造一个数组 b b b,使得 a i + b i a_i + b_i ai+bi 构成非递减序列,并且数组 b b b 的极差(最大值减最小值)尽可能小。要求输出任意一个满足条件的 b b b 数组。
1. 问题等价转化
- 设最终序列为 f i = a i + b i f_i = a_i + b_i fi=ai+bi。要求 f f f 非递减,即 f 1 ≤ f 2 ≤ ⋯ ≤ f n f_1 \le f_2 \le \dots \le f_n f1≤f2≤⋯≤fn。
- 数组 b b b 的极差为 max ( b i ) − min ( b i ) = max ( f i − a i ) − min ( f i − a i ) \max(b_i) - \min(b_i) = \max(f_i - a_i) - \min(f_i - a_i) max(bi)−min(bi)=max(fi−ai)−min(fi−ai)。
- 由于我们可以对 b b b 整体平移(同时加上或减去一个常数),极差不变,因此不妨令 min ( b i ) = 0 \min(b_i) = 0 min(bi)=0,即存在某个 i i i 使得 b i = 0 b_i = 0 bi=0,从而 f i = a i f_i = a_i fi=ai。
- 为了使
f
f
f 非递减且
b
i
≥
0
b_i \ge 0
bi≥0,一个自然的构造是令
f
i
f_i
fi 等于
a
a
a 的前缀最大值:
f i = pmx i = max j ≤ i a j f_i = \text{pmx}_i = \max_{j \le i} a_j fi=pmxi=j≤imaxaj
这样 f i f_i fi 显然非递减,且 f i ≥ a i f_i \ge a_i fi≥ai,所以 b i = pmx i − a i ≥ 0 b_i = \text{pmx}_i - a_i \ge 0 bi=pmxi−ai≥0。当 a i a_i ai 本身是前缀最大值时, b i = 0 b_i = 0 bi=0,因此 min ( b i ) = 0 \min(b_i) = 0 min(bi)=0。 - 此时
b
b
b 的极差为:
max i b i − min i b i = max i ( pmx i − a i ) − 0 = max i ( pmx i − a i ) \max_i b_i - \min_i b_i = \max_i (\text{pmx}_i - a_i) - 0 = \max_i (\text{pmx}_i - a_i) imaxbi−iminbi=imax(pmxi−ai)−0=imax(pmxi−ai)
可以证明,这个值就是所有合法构造中极差的最小值(与另一种后缀最小值构造得到的极差相等)。因此该构造即为最优解。
2. 算法实现
- 读入 n n n 和数组 a a a。
- 初始化
mx为一个极小值(如-INF),用于记录当前前缀最大值。 - 遍历
i
=
1
∼
n
i = 1 \sim n
i=1∼n:
- 更新
mx = max(mx, a[i])。 - 计算
b[i] = mx - a[i]。
- 更新
- 输出数组
b。
3. 复杂度分析
- 时间复杂度:只需一次线性遍历, O ( n ) O(n) O(n)。 n ≤ 2 × 10 5 n \le 2 \times 10^5 n≤2×105,非常快。
- 空间复杂度:需要存储数组 a a a 和 b b b, O ( n ) O(n) O(n)。也可以边读边算,只存 b b b。
总结
通过令最终序列等于原数组的前缀最大值,构造出的 b i = pmx i − a i b_i = \text{pmx}_i - a_i bi=pmxi−ai 保证了最终序列非递减,且 b i ≥ 0 b_i \ge 0 bi≥0,最小值必为 0 0 0。此时极差等于最大的前缀差值,该值被证明是最小的。算法简单高效,直接输出即可。
代码简要说明
- 使用
long long存储数组,防止溢出。 INF取0x3f3f3f3f3f3f3f3f作为极小值初始化mx。- 遍历数组,维护前缀最大值
mx,同时计算b[i] = mx - a[i]。 - 最后按顺序输出
b数组,空格分隔。
代码内容
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef vector<vector<ll>> vvt;
typedef pair<ll,ll> pll;
const ll N=1e3+10;
const ll INF=0x3f3f3f3f3f3f3f3f;
const ll M=1e6+10;
const ll mod=1000000007;
ll a[200005];
ll b[200005];
void solve()
{
ll n;
cin>>n;
for(ll i=1;i<=n;i++) cin>>a[i];
ll mx=-INF;
for(ll i=1;i<=n;i++)
{
if(a[i]>mx) mx=a[i];
b[i]=mx-a[i];
}
for(ll i=1;i<=n;i++) cout<<b[i]<<" ";
cout<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int t=1;
while(t--) solve();
return 0;
}
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/2301_80065123/article/details/167033379




