「题解」Codeforces 442C Artem and Array

考虑三个相邻的元素 (x,y,z) 满足 (yleq 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 yleq 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
", ans-mx-bmx);
	return 0;
}
原文地址:https://www.cnblogs.com/do-while-true/p/15437168.html