T1:外卖

你当然可以通过直接全排列枚举走的顺序通过本题,但那样太麻烦了

其实本题可以分为以下三种情况:

  1. A 点在最左侧,那么最短距离 = 最大值 最小值
  2. A 点在最右侧,那么最短距离 = 最大值 最小值
  3. A 点在中间,那么最短距离 = 最大值 最小值 + min( 最大值 A,最小值 A)

image

代码实现
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
freopen("take_out.in", "r", stdin);
freopen("take_out.out", "w", stdout);
int a, b, c, d;
cin >> a >> b >> c >> d;
int mn = min(min(a, b), min(c, d));
int mx = max(max(a, b), max(c, d));
ll ans = mx-mn;
if (mn < a and a < mx) ans += min(a-mn, mx-a);
cout << ans << '\n';
return 0;
}

T2:区间问题 I

首先,给定 l,r,如何快速计算 i=lrai2i=lraik(rl+1) ?

f(x)=i=1xai2i=1xaikx=i=1x(ai2aik)

那么我们可以发现,f(x)ai2aik 的前缀和,所以原式可以写作 f(r)f(l1)

想要让 f(r)f(l1) 尽可能大,应该最小化 f(l1),因此在计算 f(i) 时,还需维护 mn=min0ji1f(j),因此答案为 max(f(i)mn)

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
int main() {
freopen("interval.in", "r", stdin);
freopen("interval.out", "w", stdout);
int n, k;
cin >> n >> k;
vector<ll> a(n);
rep(i, n) cin >> a[i];
vector<ll> b(n);
rep(i, n) b[i] = a[i]*a[i] - a[i] - k;
vector<ll> s(n);
rep(i, n) s[i+1] = s[i]+b[i];
ll ans = -1e18, mn = 0;
for (int i = 1; i <= n; ++i) {
ans = max(ans, s[i]-mn);
mn = min(mn, s[i]);
}
cout << ans << '\n';
return 0;
}