0%

2019B组-J.灵能传输

J. 灵能传输

题目描述:

给出一个数组 $ n $ 个元素,对于 $ i \in [2, n - 1] $ 有以下两种操作

  • 如果 $ a_i \ge 0 $ , $ a_{i-1} + a_i, a_i - 2 \times a_i, a_{i + 1} + a_i $
  • 如果 $ a_i \lt 0 $ , $ a_{i-1} - |a_i|, a_i + 2 \times |a_i|, a_{i + 1} - |a_i| $

进行若干次操作,使得 $ max_{i = 1}^n|a_i| $ 最小,输出最小值

数据范围:
$ 1\le T \le 3, 3 \le n \le 3\times 10^5 \\\\ |a_i| \le 10 ^ 9 $

题解:

这个题目转化很关键,如果看到标签,就会简单许多。

首先这个表示形式可以进行统一: $ a_{i-1} + a_i, a_i - 2 \times a_i, a_{i + 1} + a_i $ ,观察对前缀和序列的影响

发现这个操作实际上是对前缀和序列进行两项交换 $ (s_i, s_{i-1}) $ ,由于题目中给出的下标范围,所以是不涉及 $ s_n $ 的。通过交换 $ s_i $ 得到最后的序列,然后相邻的作差得到实际序列。加入 $ s_0 = 0 $ ,补全一致性, $ s_0 $ 是起点, $ s_n $ 是终点。一个贪心的想法就是将所有的元素 $ s_i $ 直接排序,然后从起点到终点找到一种方式可以遍历所有的元素。

JPEG图像

代码:

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#define ll long long
using namespace std;
const int mod = 1e4;
const int maxn = 3e5 + 10;
int n, m, k;
ll sum[maxn];
bool flag[maxn];
ll ans[maxn];
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif // ONLINE_JUDGE
ios::sync_with_stdio(false);
cin.tie(0);
int tcase;
cin >> tcase;
while(tcase--)
{
cin >> n;
sum[0] = 0;
for(int i = 1,x; i <= n; i++)
{
cin >> x;
sum[i] = x + sum[i-1];
flag[i] = flag[i - 1] = 0;
}
ll s0 = 0, sn = sum[n];
if(s0 > sn) swap(s0, sn);
int index0, indexn;
sort(sum, sum + 1 + n);
for(int i = 0; i <= n; i++)
{
if(sum[i] == s0) index0 = i;
if(sum[i] == sn) indexn = i;
}
int l = 0, r = n;
for(int i = index0; i >= 0; i -= 2)
{
ans[l++] = sum[i], flag[i] = 1;
}
for(int i = indexn; i <= n; i += 2)
{
ans[r--] = sum[i], flag[i] = 1;
}
for(int i = 0; i <= n; i++)
{
if(!flag[i]) ans[l++] = sum[i];
}
ll maxx = 0;
for(int i = 1; i <= n; i++)
{
maxx = max(abs(ans[i] - ans[i - 1]), maxx);
}
cout << maxx << endl;
}
return 0;
}