CF1798D Shocking Arrangement 题解
参考了扶苏的证明,看起来很直觉,证明有点不明觉厉。
我们考虑这样一种构造,考虑增量,直接维护当前答案序列的和 \(s\):
- 当 \(s \ge 0\) 时,随便选一个 \(x(x \le 0)\) 放到序列末尾。
- 当 \(s \le 0\) 时,随便选一个 \(x(x \ge 0)\) 放到序列末尾。
证明是这样的,因为我们插入的都是与 \(s\) 异号的数,那么显然有 \(\min_{i=1}^n a_i < s < \max_{i=1}^n a_i\),再考虑所有区间的和,在对序列做前缀和之后,都是两个前缀和相减,即 \(s_r - s_l\) 的形式。
那么,因为 \(\forall s_r < \max_{i=1}^n a_i\),并且 \(\forall s_l > min_{i=1}^n a_i\),那么将两式相减,得到 \(|s_r - s_l| < \max - \min\),这里需要一点分讨,读者自证不难。
考虑无解,全 \(0\) 序列显然无解,充分性显然。必要性通过上面的构造性证明,易得只要序列存在任意一个非 \(0\) 数,就能构造出答案。
// 如果命运对你缄默, 那就活给他看。
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast", "inline", "-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
// #define int LL
const int maxn = 3e5 + 10;
vector<int>ans;
int a[maxn], n;
inline void ed() {
cout << "No\n";
}
inline void solve() {
cin >> n;
queue<int>q[2];
for(int i = 1; i <= n; ++ i) {
cin >> a[i];
q[a[i] >= 0].emplace(a[i]);
}
if(!*max_element(a + 1, a + 1 + n)) return cout << "No\n", void();
LL s = 0;
vector<int>ans;
while(ans.size() < n) {
int x;
if(q[s < 0].empty()) x = q[s >= 0].front(), q[s >= 0].pop();
else x = q[s < 0].front(), q[s < 0].pop();
s += x, ans.emplace_back(x);
}
cout << "Yes\n";
for(int x : ans) cout << x << ' '; cout << '\n';
}
signed main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while(T -- ) solve();
return 0;
}

浙公网安备 33010602011771号