1 条题解
-
0
思路与算法
先考虑除全局最小值以外的起点。全局最小值必在最后被拿走;把它取掉后旋转成线性序列,两端的外部边界均是这个最小值。单调栈建立最小笛卡尔树。起点u所在子树的所有数都比两侧祖先边界大,因此该子树会先被消费,外展过程是把左侧逆序和右侧正序按当前较大前沿合并。记录整个删除序列的交错和D=第1项-第2项+第3项-…,则先手分数=(全部总和+D)/2。预处理原序列的交错前缀和,连续区间无论正序或逆序都可O(1)求交错和。求u子树内部交错和时,逐个扫描较短一侧;在线段树上寻找较长一侧第一个比当前短侧前沿更小的位置,将此前连续大段一次加入,再加入短侧前沿。所有结点的较短侧总长度O(n log n)。子树消费完后,从父亲到另一侧子树的序列是一个连续的正序或逆序区间,按长度奇偶沿父子关系计算外部交错和。合并内外即可回答全部非最小值起点。最小值作为第一步时例外,直接进行一次圆周双端模拟O(n)。
正确性说明
最小值从不优于另一个候选,故除首步选它外必最后删除。最小笛卡尔树子树是连续区间,区间内所有值大于其两侧外界祖先;从区间内起步,边界外的数无法抢在区间内候选之前被选,因此先完整消费自身子树。区间内左右暴露的候选顺序固定为左逆序、右正序,线段树跳过的长侧连续段都严格大于短侧当前值,逐段合并与题定逐步比较完全一致。子树消费后必须先取父亲,再沿另一侧子树靠近端依次消耗,直至转入更外层;外部递推因此精确。交错和拼接规律是D(XY)=D(X)+(-1)^|X|D(Y),用于各段、各层和内外合并,故所得D就是真实删除序列交错和。全部总和与交错和相加恰为先手所得的两倍。最小值首步另以原规则直接模拟,所以覆盖全部起点。
复杂度
令D为输入数及中间和的最大十进制位数。单调栈建树、遍历和交错前缀初始化O(nD),线段树初始化O(nD)。每个结点扫描较短孩子区间,按small-to-large计费总扫描O(n log n),每次找阈值边界O(log n)次比较,所以总O(n log²n·D),空间O(nD)。当所有数的绝对值不超过10^12时用int64分支,n<=300000保证加减安全,数值运算视为O(1);否则启用内联精确大整数。全局最小值首步的单次模拟另O(nD)。
易错点
Ai仅规定上界,不能假设非负;大负数必须精确运算。N=1直接输出本数。每次取数规则完全确定,不能改成两人博弈取最优。连续段逆序时交错和的首项符号由右端下标决定。外部串长度和子树大小的奇偶必须分别处理。总和+D为偶数,即便结果为负也无需浮点除法。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; struct cpp_int { string s; bool neg; cpp_int(long long v = 0) : s(to_string(v)), neg(v < 0) { if (neg) s.erase(s.begin()); } void norm() { auto p = s.find_first_not_of('0'); s = p == string::npos ? "0" : s.substr(p); if (s == "0") neg = false; } static int cmpabs(const cpp_int &a, const cpp_int &b) { if (a.s.size() != b.s.size()) return a.s.size() < b.s.size() ? -1 : 1; return a.s == b.s ? 0 : a.s < b.s ? -1 : 1; } friend bool operator<(const cpp_int &a, const cpp_int &b) { if (a.neg != b.neg) return a.neg; int c = cmpabs(a, b); return a.neg ? c > 0 : c < 0; } friend bool operator==(const cpp_int &a, const cpp_int &b) { return a.neg == b.neg && a.s == b.s; } friend bool operator!=(const cpp_int &a, const cpp_int &b) { return !(a == b); } friend bool operator>(const cpp_int &a, const cpp_int &b) { return b < a; } friend bool operator<=(const cpp_int &a, const cpp_int &b) { return !(b < a); } friend bool operator>=(const cpp_int &a, const cpp_int &b) { return !(a < b); } cpp_int operator-() const { cpp_int r = *this; if (r.s != "0") r.neg = !r.neg; return r; } friend cpp_int operator+(const cpp_int &a, const cpp_int &b) { cpp_int r; r.s.clear(); int carry = 0; if (a.neg == b.neg) { int i = (int)a.s.size() - 1, j = (int)b.s.size() - 1; while (i >= 0 || j >= 0 || carry) { int v = carry + (i >= 0 ? a.s[i--] - '0' : 0) + (j >= 0 ? b.s[j--] - '0' : 0); r.s += char('0' + v % 10); carry = v / 10; } r.neg = a.neg; } else { const cpp_int *x = &a, *y = &b; if (cmpabs(a, b) < 0) swap(x, y); int i = (int)x->s.size() - 1, j = (int)y->s.size() - 1; while (i >= 0) { int v = x->s[i--] - '0' - carry - (j >= 0 ? y->s[j--] - '0' : 0); carry = v < 0; if (carry) v += 10; r.s += char('0' + v); } r.neg = x->neg; } reverse(r.s.begin(), r.s.end()); r.norm(); return r; } friend cpp_int operator-(const cpp_int &a, const cpp_int &b) { return a + (-b); } friend cpp_int operator*(const cpp_int &a, int b) { cpp_int r; r.s.clear(); long long carry = 0; long long z = b; if (z < 0) z = -z; for (int i = (int)a.s.size() - 1; i >= 0; i--) { carry += (a.s[i] - '0') * z; r.s += char('0' + carry % 10); carry /= 10; } while (carry) { r.s += char('0' + carry % 10); carry /= 10; } reverse(r.s.begin(), r.s.end()); r.neg = a.neg ^ (b < 0); r.norm(); return r; } friend cpp_int operator/(const cpp_int &a, int b) { cpp_int r; r.s.clear(); long long rem = 0, z = b; if (z < 0) z = -z; for (char c : a.s) { rem = rem * 10 + c - '0'; r.s += char('0' + rem / z); rem %= z; } r.neg = a.neg ^ (b < 0); r.norm(); return r; } cpp_int &operator+=(const cpp_int &b) { *this = *this + b; return *this; } cpp_int &operator-=(const cpp_int &b) { *this = *this - b; return *this; } cpp_int &operator*=(int b) { *this = *this * b; return *this; } friend istream &operator>>(istream &in, cpp_int &a) { string v; if (in >> v) { a.neg = v[0] == '-'; if (v[0] == '-' || v[0] == '+') v.erase(v.begin()); a.s = v; a.norm(); } return in; } friend ostream &operator<<(ostream &out, const cpp_int &a) { if (a.neg) out << '-'; return out << a.s; } }; // The range-minimum tree finds the first/last value smaller than a threshold. template <class I> struct Solver { int n, k, root, p; vector<I> a, pref, inside, ext; vector<int> l, r, par, lo, hi, sz, mn; vector<I> orig; Solver(vector<I> v) : n(v.size()), orig(v) { } I alt(int x, int y, bool reverse = false) { if (x > y) return I(0); I z = pref[y + 1] - pref[x]; return ((reverse ? y : x) & 1) ? -z : z; } int findbelow(int z, int L, int R, int ql, int qr, const I &v, bool rev) { if (R < ql || qr < L || mn[z] < 0 || !(a[mn[z]] < v)) return -1; if (L == R) return L; int M = (L + R) / 2, first = rev ? z * 2 + 1 : z * 2, second = rev ? z * 2 : z * 2 + 1; int t = rev ? findbelow(first, M + 1, R, ql, qr, v, rev) : findbelow(first, L, M, ql, qr, v, rev); if (t >= 0) return t; return rev ? findbelow(second, L, M, ql, qr, v, rev) : findbelow(second, M + 1, R, ql, qr, v, rev); } I mergealt(int u) { int L = lo[u], R = hi[u], nl = u - L, nr = R - u; if (!nl) return a[u] - alt(u + 1, R); if (!nr) return a[u] - alt(L, u - 1, true); bool smallleft = nl < nr; int s = smallleft ? u - 1 : u + 1, se = smallleft ? L : R, sd = smallleft ? -1 : 1; int b = smallleft ? u + 1 : u - 1, be = smallleft ? R : L, bd = smallleft ? 1 : -1; I ans = a[u]; int parity = 1; auto add = [&](I z, int len) { if (parity) ans -= z; else ans += z; parity ^= (len & 1); }; while ((sd > 0 ? s <= se : s >= se) && (bd > 0 ? b <= be : b >= be)) { int ql = min(b, be), qr = max(b, be); int t = findbelow(1, 0, k - 1, ql, qr, a[s], bd < 0); if (t < 0) { add(alt(ql, qr, bd < 0), abs(be - b) + 1); b = be + bd; break; } if (t != b) { int e = t - bd; add(alt(min(b, e), max(b, e), bd < 0), abs(e - b) + 1); b = t; } add(a[s], 1); s += sd; } if (sd > 0 ? s <= se : s >= se) add(alt(min(s, se), max(s, se), sd < 0), abs(se - s) + 1); if (bd > 0 ? b <= be : b >= be) add(alt(min(b, be), max(b, be), bd < 0), abs(be - b) + 1); return ans; } void run() { if (n == 1) { cout << orig[0] << '\n'; return; } p = min_element(orig.begin(), orig.end()) - orig.begin(); for (int j = 1; j < n; j++) a.push_back(orig[(p + j) % n]); int N = n - 1; l.assign(N, -1); r = l; par = l; vector<int> st; for (int i = 0; i < N; i++) { int last = -1; while (!st.empty() && a[i] < a[st.back()]) { last = st.back(); st.pop_back(); } if (!st.empty()) { r[st.back()] = i; par[i] = st.back(); } if (last >= 0) { l[i] = last; par[last] = i; } st.push_back(i); } root = st[0]; vector<int> order(1, root); for (size_t j = 0; j < order.size(); j++) { int u = order[j]; if (l[u] >= 0) order.push_back(l[u]); if (r[u] >= 0) order.push_back(r[u]); } lo.resize(N); hi.resize(N); sz.resize(N); inside.resize(N); ext.resize(N); pref.assign(N + 1, I(0)); for (int i = 0; i < N; i++) pref[i + 1] = pref[i] + (i & 1 ? -a[i] : a[i]); k = 1; while (k < N) k *= 2; mn.assign(k * 2, -1); for (int i = 0; i < N; i++) mn[k + i] = i; for (int j = k - 1; j; j--) { int x = mn[j * 2], y = mn[j * 2 + 1]; mn[j] = x < 0 ? y : y < 0 ? x : a[x] < a[y] ? x : y; } for (int j = N - 1; j >= 0; j--) { int u = order[j]; lo[u] = l[u] < 0 ? u : lo[l[u]]; hi[u] = r[u] < 0 ? u : hi[r[u]]; sz[u] = hi[u] - lo[u] + 1; inside[u] = mergealt(u); } ext[root] = orig[p]; for (int u : order) { if (l[u] >= 0) { I z = a[u] - alt(u + 1, hi[u]); int len = 1 + hi[u] - u; ext[l[u]] = z + (len & 1 ? -ext[u] : ext[u]); } if (r[u] >= 0) { I z = a[u] - alt(lo[u], u - 1, true); int len = 1 + u - lo[u]; ext[r[u]] = z + (len & 1 ? -ext[u] : ext[u]); } } I total = 0; for (I x : orig) total += x; vector<I> ans(n); for (int i = 0; i < N; i++) { I diff = inside[i] + (sz[i] & 1 ? -ext[i] : ext[i]); ans[(p + i + 1) % n] = (total + diff) / 2; } I d = orig[p]; int L = 0, R = N - 1, turn = 1; while (L <= R) { I x; if (a[R] < a[L]) x = a[L++]; else x = a[R--]; if (turn) d -= x; else d += x; turn ^= 1; } ans[p] = (total + d) / 2; for (I x : ans) cout << x << '\n'; } }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; vector<string> s(n); bool small = true; for (string &x : s) { cin >> x; string z = x; if (z[0] == '-' || z[0] == '+') z.erase(z.begin()); size_t f = z.find_first_not_of('0'); z = f == string::npos ? "0" : z.substr(f); if (z.size() > 12) small = false; } if (small) { vector<long long> v; for (auto x : s) v.push_back(stoll(x)); Solver<long long>(v).run(); } else { vector<cpp_int> v(n); for (int i = 0; i < n; i++) { istringstream in(s[i]); in >> v[i]; } Solver<cpp_int>(v).run(); } }
- 1