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;
}
posted @ 2025-10-28 11:20  Rainsheep  阅读(4)  评论(0)    收藏  举报