「題解」Codeforces 442C Artem and Array
阿新 • • 發佈:2021-10-22
人類智慧貪心題
考慮三個相鄰的元素 \(x,y,z\) 滿足 \(y\leq x,z\),則三者先刪除 \(y\) 更優。
假設最左側和最右側有兩個不能刪除的 \(0\),發現和原問題等價,這樣就不需要討論邊界情況了。
考慮反證,不失一般性假設先刪除 \(x\).對於任意一種先刪除 \(x\) 的策略,在刪除 \(x\) 的時候先刪除 \(y\) 一定不劣:
假設現在序列是 \(...,v,x,y,z,...\):
- 如果先刪除 \(x\),對答案貢獻是 \(\min(v,y)\),剩餘序列為 \(...,v,y,z,...\);
- 如果先刪除 \(y\),對答案貢獻是 \(\min(x,z)\),剩餘序列為 \(...,v,x,z,...\)
注意到 \(\min(v,y)\leq y\leq \min(x,z)\),以及序列中的數越大答案越大,所以後者相比前者不劣。
然後變成雙調的,答案為 \(n-2\) 小的數的和。雖然前面編出來證明了,這裡的構造還是w23c3c3教我的/ll
性質:最大值和次大值一定不會對答案做貢獻,所以它們具體取值對答案無所謂,存在的意義僅為比其他所有的都大。
充分:給出一種構造,如果第三大值在最大值旁邊,則刪去最大值,否則第三大值在次大值旁邊,刪去次大值,遞迴成子問題。
必要:嘗試構造,發現一個數如果對答案貢獻 \(x\) 次,那麼一定有一個比它大的數少貢獻 \((x-1)\) 次,所以每個數僅貢獻一次最優。
#include<iostream> #include<cstdio> #include<algorithm> #include<vector> #include<queue> #include<set> #define pb emplace_back #define mp std::make_pair #define fi first #define se second typedef long long ll; typedef long double ld; typedef std::pair<int, int> pii; typedef std::pair<ll, int> pli; typedef std::pair<ll, ll> pll; typedef std::vector<int> vi; typedef std::vector<pii> vpii; typedef std::vector<ll> vll; const ll mod = 998244353; ll Add(ll x, ll y) { return (x+y>=mod) ? (x+y-mod) : (x+y); } ll Mul(ll x, ll y) { return x * y % mod; } ll Mod(ll x) { return x < 0 ? (x + mod) : (x >= mod ? (x-mod) : x); } ll cadd(ll &x, ll y) { return x = (x+y>=mod) ? (x+y-mod) : (x+y); } template <typename T> T Max(T x, T y) { return x > y ? x : y; } template <typename T> T Min(T x, T y) { return x < y ? x : y; } template <typename T> T &read(T &r) { r = 0; bool w = 0; char ch = getchar(); while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar(); while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar(); return r = w ? -r : r; } const int N = 500010; int n, a[N], l[N], r[N], vis[N], b[N], mx, bmx; ll ans; std::queue<int>q; bool check(int x) { return x && !vis[x] && a[x] <= a[l[x]] && a[x] <= a[r[x]]; } signed main() { read(n); for(int i = 1; i <= n; ++i) read(a[i]), l[i] = i-1, r[i] = i+1; r[n] = 0; for(int i = 2; i < n; ++i) if(check(i)) q.push(i), vis[i] = 1; while(!q.empty()) { int x = q.front(); q.pop(); ans += Min(a[l[x]], a[r[x]]); r[l[x]] = r[x]; l[r[x]] = l[x]; if(check(l[x])) q.push(l[x]), vis[l[x]] = 1; if(check(r[x])) q.push(r[x]), vis[r[x]] = 1; } for(int i = 1; i <= n; ++i) if(!vis[i]) { ans += a[i]; if(a[i] >= mx) bmx = mx, mx = a[i]; else bmx = Max(bmx, a[i]); } printf("%lld\n", ans-mx-bmx); return 0; }