[DP记录]AT2274 [ARC066D] Contest with Drinks Hard

command_block

2021-05-13 15:29:50

Personal

**题意** : Joisino 要去打 final。这场比赛中有 $n$ 道题,Joisino 做第 $i$ 道题花费的时间为 $t_i$ 。 这场比赛中,选手的做题方式是选择自己想做的题来做(可以不做),并且一定能做出来。最后,选手的得分将以如下方式计算: $$ \text{得分} = \text{满足条件的二元组 $(l, r)$ 的个数} - \text{解决选了的题所花费的时间} $$ 其中,二元组 $(l, r)$ 需要满足的条件是:对于所有满足 $l \le i \le r$ 的 $i$ ,第 $i$ 题都做了。另外, $l$ 和 $r$ 还需满足 $1 \le l \le r \le n$ 。 主办方为参赛者提供了 $M$ 种饮料,如果 Joisino 喝了第 $i$ 种饮料,他做第 $p_i$ 题时会兴奋,做第 $p_i$ 题的时间将从 $t_{p_i}$ 变成 $x_i$ (注意 $x_i$ 不一定比 $t_{p_i}$ 小)做其它题的时间不受影响。 一位参赛者只能带一种饮料进入考场。Joisino 想知道如果他喝下了每种饮料,他的最大得分。 $n,m\leq 3\times 10^5$ ,时限$\texttt{2s}$。 ------------ 先不考虑饮料。 不难发现,对于一个长为 $l$ 的 AC 的题目的极大连续段,对答案的贡献为 $\dfrac{l(l+1)}{2}$。 记 $f[i]$ 表示考虑前 $i$ 题能得到的最大收益,$st_i$ 为 $t_i$ 的前缀和。 枚举最后一个极长连续段,有转移 : $$ \begin{aligned} f[k]&=\max\limits_{i=0}^{k-1}\tfrac{(k-i)(k-i+1)}{2}-(st_k-st_i)+f[i]\\ &=\frac{1}{2}\max\limits_{i=0}^{k-1}(k-i)(k-i+1)-2(st_k-st_i)+2f[i]\\ &=\frac{1}{2}\bigg(k^2+k-2st_k+\max\limits_{i=0}^{k-1}-2ki+\big(i^2-i+2st_i+2f[i]\big)\bigg) \end{aligned} $$ 观察后面的 $\max\limits_{i=0}^{k-1}-2ki+\big(i^2-i+2st_i+2f[i]\big)$ ,已经是可以斜率优化的形式。 然后考虑如何处理饮料的单点修改。 记 $f_i$ 表示只做题目 $f_{1\sim i}$ 能得到的最大收益, $g_i$ 表示只做题目 $i\sim n$ 能得到的最大收益,$h_i$ 表示必做题目 $i$ 所能得到的最大收益。 对于询问 $(p,x)$ ,分做或不做题目 $i$ 讨论,答案为 $\max\big(f_{i-1}+g_{i+1},h_i+t_i-x\big)$ $f,g$ 这俩货可以用前面的斜率优化 $\rm DP$ 预处理。 $h$ 用分治计算。 考虑包含 $i$ 的极长连续段,对于连续段 $[l,r]$ ,贡献为 : $$\tfrac{(r-l+1)(r-l+2)}{2}-(st_r-st_{l-1})+f_{l-1}+g_{r+1}$$ 在 $solve(l,r)$ 中处理 $[l,r]$ 的所有子区间的贡献。 每次处理跨越 $mid$ 的区间对询问的贡献。 处理 $i\in(mid,r]$ 的 $h_i$ 。此时要求左端点 $\in [l,mid]$ ,右端点 $\in [p_i,r]$。 (对 $i\in [l,mid]$ 的部分,翻转序列重做即可) 记 $h'_i$ 为此时区间右端点恰好在 $i$ 的最优解。则有 : $$ \begin{aligned} h_i'&=\max_{j+1\in[l,mid]}\tfrac{(i-j)(i-j+1)}{2}-(st_i-st_j)+f_j+g_{i+1}\\ &=g_{i+1}-st_i+\frac{1}{2}\bigg(i^2+i+\max_{j+1\in[l,mid]}-2ij+\big(j^2-j+2st_j+2f_j\big)\bigg)\\ \end{aligned} $$ 其中 $\max\limits_{j+1\in[l,mid]}-2ij+\big(j^2-j+2st_j+2f_j\big)$ 仍然是可以斜率优化的形式。线性求出凸壳,再线性扫描即可。 计算完成后, $h'_i$ 能向 $h_{mid+1\sim i}$ 贡献。 复杂度 $O\big((n+q)\log n\big)$。 ```cpp #include<algorithm> #include<cstdio> #define ll long long #define MaxN 300500 using namespace std; struct Line{ int k;ll b; ll get(int x){return 1ll*k*x+b;} }q[MaxN]; bool chk(Line A,Line B,Line C){ int x1=A.k-B.k,x2=C.k-B.k; ll y1=A.b-B.b,y2=C.b-B.b; return x1*y2-x2*y1>=0; } ll st[MaxN]; void calc(ll *t,ll *f,int n) { int top=1;q[1]=(Line){0,0}; for (int i=1;i<=n;i++){ st[i]=st[i-1]+t[i]; while(top>1&&q[top].get(i)<=q[top-1].get(i))top--; f[i]=max(f[i-1],-st[i]+(q[top].get(i)+1ll*i*(i+1))/2); Line now=(Line){-2*i,1ll*i*(i-1)+2*(f[i]+st[i])}; while(top>1&&chk(q[top-1],q[top],now))top--; q[++top]=now; } } ll ans[MaxN],t[MaxN],fl[MaxN],fr[MaxN],sc[MaxN]; bool flag; void solve(int l,int r) { if (l==r)return ; int mid=flag ? (l+r)>>1 : (l+r-1)>>1; solve(l,mid);solve(mid+1,r); int top=0; for (int i=l-1;i<mid;i++){ Line now=(Line){-2*i,1ll*i*(i-1)+2*(st[i]+fl[i])}; while(top>1&&chk(q[top-1],q[top],now))top--; q[++top]=now; } for (int i=mid+1;i<=r;i++){ while(top>1&&q[top].get(i)<=q[top-1].get(i))top--; sc[i]=fr[i+1]-st[i]+(q[top].get(i)+1ll*i*(i+1))/2; } sc[r+1]=-1ll<<60; for (int i=r;i>mid;i--) ans[i]=max(ans[i],sc[i]=max(sc[i],sc[i+1])); } int n,m; int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%lld",&t[i]); calc(t,fr,n);reverse(fr+1,fr+n+1); reverse(t+1,t+n+1);calc(t,fl,n); for (int i=1;i<=n;i++)ans[i]=-1ll<<60; solve(1,n); reverse(ans+1,ans+n+1);reverse(t+1,t+n+1); for (int i=1;i<=n;i++)st[i]=st[i-1]+t[i]; for (int i=1;i<=n;i++)swap(fl[i],fr[i]); reverse(fl+1,fl+n+1);reverse(fr+1,fr+n+1); flag=1;solve(1,n); scanf("%d",&m); for (int i=1,p,x;i<=m;i++){ scanf("%d%d",&p,&x); printf("%lld\n",max(fl[p-1]+fr[p+1],ans[p]+t[p]-x)); }return 0; } ```