do_while_true

一言(ヒトコト)

「题解」Codeforces 442C Artem and Array

考虑三个相邻的元素 \(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;
}
posted @ 2021-10-22 09:16  do_while_true  阅读(57)  评论(0编辑  收藏  举报