先考虑不修改怎么做,可以令 \(dp_i\) 表示前 \(i\) 个题能获得的最大得分。那么我们有转移:

\[dp_i = \min\{dp_{i - 1}, dp_{j} + \frac{(i - j + 1)(i - j)}{2} - (S_i - S_j)\}(0 \le j < i)
\]

很显然后面那部分是一个斜优的形式,我们化一下柿子可以得到:

\[dp_j + \frac{j(j - 1)}{2} + S_j = ij + dp_i + S_i - \frac{i(i + 1)}{2}
\]

可以发现我们需要维护一个上凸包的形式,每次查询的斜率单调递增,加入凸包的点单调递增,可以使用单调栈完成这个操作。

接下来再考虑修改,不难发现每次我们修改的这个位置可能是选或不选两种状态,对于后者我们只需维护出 \(f_i\) 表示 \(1 \sim i\) 这些题能获得的最大得分和 \(g_i\) 表示 \(i \sim n\) 这些题的最大得分,那么不选这个题的最大得分就是 \(f_{i - 1} + g_{i + 1}\)。

接下来我们考虑选择这个位置的情况。令 \(h_i\) 表示强制选择 \(i\) 这个位置的最大得分,考虑枚举左边第一个没有选择的位置 \(l\),和右边第一个选择的位置 \(r\)(这样好算一些),那么有转移:

\[h_i = \min\{f_l + g_{r + 1} + \frac{(r - l + 1)(r - l)}{2} - (S_r - S_l)\}(0 \le l < i, i \le r \le n)
\]

根据这个柿子我们可以发现这样一个事实,如果我们每次修改了一个 \(T_i\) 对 \(S_l, f_l, g_{r + 1}\) 是不会有影响的,对 \(S_r\) 相当于整体加上了一个变化量,因此我们可以提前预处理出最开始序列的 \(h_i\) 那么修改某个点后必选这个位置的最大得分就是 \(h_i + \Delta\) 了。

继续考虑上面那个式子,我们化简一下式子看看这个式子还能不能斜率优化:

\[g_{r + 1} + \frac{r(r + 1)}{2} - S_r = lr + h_i - f_l - \frac{l(l - 1)}{2} - S_l
\]

可以发现我们在枚举 \(i\) 同时枚举 \(l\),那么上面的那个式子相当于也是一个斜率优化的形式,这样我们就可以做到 \(O(n ^ 2)\) 了,但这和暴力重构的复杂度是一样的,我们还需另辟蹊径。

反过来想,我们不枚举每个 \(i\),而是直接考虑 \(l, r\) 对中间每个 \(i\) 的贡献。可以发现这样的 \((l, r)\) 其实是一个点对的形式,于是我们可以考虑使用 \(\rm CDQ\) 分治来优化这个过程。

具体来讲,假设当前分治考虑的区间为 \([L, R]\),那么我们当前值考虑 \(l\) 在 \([L, Mid]\),\(r\) 在 \([Mid + 1, R]\) 的点对 \((l, R)\),这样显然是能包含所有点对的。根据上面那个式子,我们依然需要枚举每个 \(l\),只是我们惊奇地发现我们的右端点的范围其实已经确定,那么每次在枚举 \(l\) 之前我们先将右半边区间的所有 \(r\) 都插入到上凸包中,那么我们从左往右地去枚举每个 \(l\),斜率单调递增,依然可以直接使用单调栈解决。不难发现我们这里求出的每个 \(l\) 的答案可以对 \([l + 1, Mid]\) 中的所有 \(h_i\) 造成贡献,反过来每个 \(i\) 可以被 \([L - 1, i - 1]\) 的 \(l\) 造成贡献,于是我们只需要记录一个前缀 \(\max\) 就可以了。但这样我们只能计算对 \([L, Mid]\) 中的点的贡献,那么对 \([Mid + 1, R]\) 中点的贡献怎么算呢?我们只需要将这个区间反过来,将 \(r\) 看作 \(l\) 将 \(l\) 看作 \(r\) 做一遍一样的流程即可。

注意因为我们要保证斜率优化的决策单调性,因此我们每次枚举的斜率 \(l\) 要单调递增,每次插入凸包中的点 \(i\) 横坐标即下边也要单调递增,这意味着我们都要从小到大地去枚举。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define N 300000 + 5
  4. #define int long long
  5. #define mid (l + r) / 2
  6. #define inf 10000000000000000
  7. #define rep(i, l, r) for(int i = l; i <= r; ++i)
  8. #define dep(i, l, r) for(int i = r; i >= l; --i)
  9. typedef double D;
  10. int n, m, P, X, top, f[N], g[N], h[N], t[N], T[N], S1[N], S2[N], st[N], mx[N];
  11. int read(){
  12. char c; int x = 0, f = 1;
  13. c = getchar();
  14. while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
  15. while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
  16. return x * f;
  17. }
  18. int Y1(int x){
  19. return f[x] + x * (x - 1) / 2 + S1[x];
  20. }
  21. int Y2(int x){
  22. return g[x] + x * (x - 1) / 2 + S2[x];
  23. }
  24. int Y3(int x){
  25. return x * (x + 1) / 2 - S1[x] + g[x + 1];
  26. }
  27. int Y4(int x){
  28. return x * (x - 1) / 2 + S1[x] + f[x];
  29. }
  30. D slope1(int a, int b){
  31. return 1.0 * (Y1(a) - Y1(b)) / (a - b);
  32. }
  33. D slope2(int a, int b){
  34. return 1.0 * (Y2(a) - Y2(b)) / (a - b);
  35. }
  36. D slope3(int a, int b){
  37. return 1.0 * (Y3(a) - Y3(b)) / (a - b);
  38. }
  39. D slope4(int a, int b){
  40. return 1.0 * (Y4(a) - Y4(b)) / (a - b);
  41. }
  42. void solve(int l, int r){
  43. if(l == r){ h[l] = max(h[l], f[l - 1] + g[l + 1] + 1 - T[l]); return;}
  44. solve(l, mid), solve(mid + 1, r);
  45. st[top = 1] = mid + 1;
  46. rep(i, mid + 2, r){
  47. while(top > 1 && slope3(st[top], st[top - 1]) < slope3(st[top - 1], i)) --top;
  48. st[++top] = i;
  49. }
  50. rep(i, l - 1, mid - 1){
  51. while(top > 1 && i > slope3(st[top], st[top - 1])) --top;
  52. t[i] = f[i] + (st[top] - i + 1) * (st[top] - i) / 2 + g[st[top] + 1] - S1[st[top]] + S1[i];
  53. }
  54. mx[l - 1] = -inf;
  55. rep(i, l, mid) mx[i] = max(mx[i - 1], t[i - 1]), h[i] = max(h[i], mx[i]);
  56. st[top = 1] = l - 1;
  57. rep(i, l, mid - 1){
  58. while(top > 1 && slope4(st[top], st[top - 1]) < slope4(st[top - 1], i)) --top;
  59. st[++top] = i;
  60. }
  61. rep(i, mid + 1, r){
  62. while(top > 1 && i > slope4(st[top], st[top - 1])) --top;
  63. t[i] = g[i + 1] + (i - st[top] + 1) * (i - st[top]) / 2 + f[st[top]] - S1[i] + S1[st[top]];
  64. }
  65. mx[r + 1] = -inf;
  66. dep(i, mid + 1, r) mx[i] = max(mx[i + 1], t[i]), h[i] = max(h[i], mx[i]);
  67. }
  68. signed main(){
  69. n = read();
  70. rep(i, 1, n) T[i] = read(), S1[i] = S1[i - 1] + T[i], h[i] = -inf;
  71. dep(i, 1, n) S2[n - i + 1] = S2[n - i] + T[i];
  72. st[++top] = 0;
  73. rep(i, 1, n){
  74. while(top > 1 && i > slope1(st[top], st[top - 1])) --top;
  75. f[i] = max(f[i - 1], f[st[top]] + (i - st[top] + 1) * (i - st[top]) / 2 + S1[st[top]] - S1[i]);
  76. while(top > 1 && slope1(st[top], st[top - 1]) < slope1(st[top - 1], i)) --top;
  77. st[++top] = i;
  78. }
  79. st[top = 1] = 0;
  80. rep(i, 1, n){
  81. while(top > 1 && i > slope2(st[top], st[top - 1])) --top;
  82. g[i] = max(g[i - 1], g[st[top]] + (i - st[top] + 1) * (i - st[top]) / 2 + S2[st[top]] - S2[i]);
  83. while(top > 1 && slope2(st[top], st[top - 1]) < slope2(st[top - 1], i)) --top;
  84. st[++top] = i;
  85. }
  86. reverse(g + 1, g + n + 1);
  87. solve(1, n);
  88. m = read();
  89. while(m--) P = read(), X = read(), printf("%lld\n", max(f[P - 1] + g[P + 1], h[P] + T[P] - X));
  90. return 0;
  91. }

AT2274 [ARC066D] Contest with Drinks Hard的更多相关文章

  1. Contest with Drinks Easy

    /* Problem Statement Joisino is about to compete in the final round of a certain programming competi ...

  2. [arc066f]Contest with Drinks Hard

    题目大意: 有一些物品,每个买了有代价. 如果存在一个极大区间[l,r]内的物品都被买了,这个区间长度为k,可以获得的收益是k*(k+1)/2. 现在若干次询问,每次问假如修改了某个物品的价格,最大收 ...

  3. AtCoder Regular Contest 066 F Contest with Drinks Hard

    题意: 你现在有n个题目可以做,第i个题目需要的时间为t[i],你要选择其中的若干题目去做.不妨令choose[i]表示第i个题目做不做.定义cost=∑(i<=n)∑(i<=j<= ...

  4. 【ARC066】F - Contest with Drinks Hard

    题解 我写的斜率维护,放弃了我最擅长的叉积维护,然后发现叉积维护也不会爆long long哦-- 一写斜率维护我的代码就会莫名变长而且难写--行吧 我们看这题 推了推式子,发现这是个斜率的式子,但是斜 ...

  5. Arc066_F Contest with Drinks Hard

    传送门 题目大意 有一个长为$N$的序列$A$,你要构造一个长为$N$的$01$序列使得$01$序列全部由$1$组成的子串个数$-$两个序列的对应位置两两乘积之和最大,每次独立的询问给定$pos,x$ ...

  6. @atcoder - ARC066F@ Contest with Drinks Hard

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定序列 T1, T2, ... TN,你可以从中选择一些 Ti ...

  7. [atARC066F]Contest with Drinks Hard

    先不考虑修改,那么很明显即对于每一个极长的的区间,若其长度为$l$,有${l+1\choose 2}$的贡献 考虑dp去做,即$f_{i}$表示前$i$个数最大的答案,则$$f_{i}=\max(\m ...

  8. AtCoder Beginner Contest 050 ABC题

    A - Addition and Subtraction Easy Time limit : 2sec / Memory limit : 256MB Score : 100 points Proble ...

  9. 【AtCoder】ARC066

    ARC066 C - Lining Up 判断是否合法即可,合法是\(2^{\lfloor \frac{N}{2}\rfloor}\) 不合法就是0 #include <bits/stdc++. ...

随机推荐

  1. HDU 6608:Fansblog(威尔逊定理)

    Fansblog Time Limit: 2000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total Subm ...

  2. [Elasticsearch] ES聚合场景下部分结果数据未返回问题分析

    背景 在对ES某个筛选字段聚合查询,类似groupBy操作后,发现该字段新增的数据,聚合结果没有展示出来,但是用户在全文检索新增的筛选数据后,又可以查询出来, 针对该问题进行了相关排查. 排查思路 首 ...

  3. Kernel PCA for Novelty Detection

    目录 引 主要内容 的选择 数值实验 矩形框 spiral 代码 Hoffmann H. Kernel PCA for novelty detection[J]. Pattern Recognitio ...

  4. Java基础寒假作业-个人所得税计算系统

    <个人所得税计算系统>设计 一.需求说明 设计一个简易的个人所得税计算系统,通过输入个人应发工资计算出各个地区的三险(医疗保险.养老保险)一金(公积金)和个人所得税.系统需要实现用户登录. ...

  5. 编写Java程序,创建一个 Person 类,该类中有一个类成员变量 country、一个实例变量 name 和另一个实例变量 age。

    返回本章节 返回作业目录 需求说明: 创建一个 Person 类,该类中有一个类成员变量 country.一个实例变量 name 和另一个实例变量 age. country 表示地区,name 表示姓 ...

  6. 云南农业职业技术学院 / 互联网技术学院官网 HTML5+CSS3

    HTML学完后写了,有小组成员参与开发,我只写了主页,那就只贴主页的代码出来了. 作为初学者,代码写得不太好,写博客纯属记录!有问题望指导! 码云开源仓库地址:https://gitee.com/yn ...

  7. VMware客户端vSphere Web Client新建虚拟机

    1.说明 vSphere Web Client是为管理员提供的一款通用的. 基于浏览器的VMware管理工具, 能够监控并管理VMware基础设施. 由于需要登录的宿主机安装的是ESXi-6.5.0, ...

  8. CAP 6.0 版本发布通告 - 支持 OpenTelemetry

    前言 今天,我们很高兴宣布 CAP 发布 6.0 版本正式版,在这个版本中,我们主要致力于对 OpenTelemetry 提供支持,以及更好的适配 .NET 6. 那么,接下来我们具体看一下吧. 总览 ...

  9. 779. 第K个语法符号

    <找规律> <递归> 题目描述 在第一行我们写上一个 0.接下来的每一行,将前一行中的0替换为01,1替换为10. 给定行数 N 和序数 K,返回第 N 行中第 K个字符.(K ...

  10. vue中另一种路由写法

    一个项目中一级菜单是固定的,二级及其以下的菜单是动态的,直接根据文件夹结构写路由 import Vue from 'vue' import Router from 'vue-router' impor ...