AtCoder Regular Contest 066 F Contest with Drinks Hard
下文令 为原题中的 。
考虑若没有饮料,可以设 表示,考虑了前 道题,第 道题没做的最大得分。转移就枚举上一道没做的题 ,那么 形成一个连续段。设 为 的前缀和,可得:
拆项可得:
容易发现其为斜率优化形式, 是截距, 是纵坐标, 是斜率, 是横坐标。注意到斜率和横坐标都单增,所以需要使用单调栈维护上凸包。
考虑若有饮料,设饮料把 变成 。分类讨论是否使用饮料。再求一个 表示考虑了第 道题到第 道题,第 道题没做的最大得分。
若没使用饮料,答案是 ;
若使用饮料,答案是 。其中 表示必须做第 道题的最大得分。
考虑如何求 。有一个单次 的求法:
套路地,考虑分治。设当前分治区间为 ,。考虑 ,对 造成的贡献。分类讨论 在哪半边,例如若 ,就求一个 的凸包,然后遍历 ,双指针遍历凸包即可。
时间复杂度为 。
code
// Problem: F - Contest with Drinks Hard // Contest: AtCoder - AtCoder Regular Contest 066 // URL: https://atcoder.jp/contests/arc066/tasks/arc066_d // Memory Limit: 256 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> #define pb emplace_back #define fst first #define scd second #define mkp make_pair #define mems(a, x) memset((a), (x), sizeof(a)) using namespace std; typedef long long ll; typedef double db; typedef unsigned long long ull; typedef long double ldb; typedef pair<ll, ll> pii; const int maxn = 300100; ll n, m, a[maxn], b[maxn], f[maxn], g[maxn], h[maxn], stk[maxn], top; struct node { ll x, y; node(ll a = 0, ll b = 0) : x(a), y(b) {} } c[maxn]; inline node operator + (const node &a, const node &b) { return node(a.x + b.x, a.y + b.y); } inline node operator - (const node &a, const node &b) { return node(a.x - b.x, a.y - b.y); } inline ll operator * (const node &a, const node &b) { return a.x * b.y - a.y * b.x; } void dfs(int l, int r) { if (l == r) { return; } int mid = (l + r) >> 1, tot = 0, top = 0; for (int i = mid + 1; i <= r; ++i) { c[++tot] = node(i, g[i] + 1LL * i * (i - 1) / 2 - b[i - 1]); } for (int i = 1; i <= tot; ++i) { while (top > 1 && (c[i] - c[stk[top - 1]]) * (c[stk[top]] - c[stk[top - 1]]) <= 0) { --top; } stk[++top] = i; } ll mx = -1e18; // printf("l, r: %d %d %d\n", l, mid, r); for (int i = l, j = top; i <= mid; ++i) { while (j > 1 && (c[stk[j]].y - c[stk[j - 1]].y) <= (c[stk[j]].x - c[stk[j - 1]].x) * i) { --j; } int k = stk[j] + mid; h[i] = max(h[i], mx); // printf("%d %d %lld\n", i, k, f[i] + g[k] + 1LL * (k - i) * (k - i - 1) / 2 - b[k - 1] + b[i]); mx = max(mx, f[i] + g[k] + 1LL * (k - i) * (k - i - 1) / 2 - b[k - 1] + b[i]); } tot = top = 0; for (int i = l; i <= mid; ++i) { c[++tot] = node(i, f[i] + 1LL * i * (i + 1) / 2 + b[i]); } for (int i = 1; i <= tot; ++i) { while (top > 1 && (c[i] - c[stk[top - 1]]) * (c[stk[top]] - c[stk[top - 1]]) <= 0) { --top; } stk[++top] = i; } mx = -1e18; for (int i = r, j = 1; i > mid; --i) { while (j < top && (c[stk[j + 1]].y - c[stk[j]].y) >= (c[stk[j + 1]].x - c[stk[j]].x) * i) { ++j; } int k = stk[j] + l - 1; h[i] = max(h[i], mx); mx = max(mx, f[k] + g[i] + 1LL * (i - k) * (i - k - 1) / 2 - b[i - 1] + b[k]); } dfs(l, mid); dfs(mid + 1, r); } void solve() { scanf("%lld", &n); for (int i = 1; i <= n; ++i) { scanf("%lld", &a[i]); b[i] = b[i - 1] + a[i]; } scanf("%lld", &m); stk[++top] = 0; for (int i = 1; i <= n; ++i) { while (top > 1 && (c[stk[top]].y - c[stk[top - 1]].y) <= (c[stk[top]].x - c[stk[top - 1]].x) * i) { --top; } int j = stk[top]; f[i] = f[j] + 1LL * (i - j) * (i - j - 1) / 2 - b[i - 1] + b[j]; c[i] = node(i, f[i] + 1LL * i * (i + 1) / 2 + b[i]); while (top > 1 && (c[i] - c[stk[top - 1]]) * (c[stk[top]] - c[stk[top - 1]]) <= 0) { --top; } stk[++top] = i; } reverse(a + 1, a + n + 1); for (int i = 1; i <= n; ++i) { b[i] = b[i - 1] + a[i]; } mems(stk, 0); top = 0; stk[++top] = 0; for (int i = 1; i <= n; ++i) { while (top > 1 && (c[stk[top]].y - c[stk[top - 1]].y) <= (c[stk[top]].x - c[stk[top - 1]].x) * i) { --top; } int j = stk[top]; g[i] = g[j] + 1LL * (i - j) * (i - j - 1) / 2 - b[i - 1] + b[j]; c[i] = node(i, g[i] + 1LL * i * (i + 1) / 2 + b[i]); while (top > 1 && (c[i] - c[stk[top - 1]]) * (c[stk[top]] - c[stk[top - 1]]) <= 0) { --top; } stk[++top] = i; } reverse(a + 1, a + n + 1); reverse(g + 1, g + n + 1); for (int i = 1; i <= n; ++i) { b[i] = b[i - 1] + a[i]; } mems(h, -0x3f); dfs(0, n + 1); // for (int i = 1; i <= n; ++i) { // printf("h[%d] = %lld\n", i, h[i]); // } // for (int i = 1; i <= n + 1; ++i) { // printf("g[%d] = %lld\n", i, g[i]); // } while (m--) { ll x, y; scanf("%lld%lld", &x, &y); printf("%lld\n", max(f[x] + g[x], h[x] + a[x] - y)); } } int main() { int T = 1; // scanf("%d", &T); while (T--) { solve(); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
2022-10-16 线段树维护哈希