zoukankan      html  css  js  c++  java
  • [atARC066F]Contest with Drinks Hard

    先不考虑修改,那么很明显即对于每一个极长的的区间,若其长度为$l$,有${l+1choose 2}$的贡献

    考虑dp去做,即$f_{i}$表示前$i$个数最大的答案,则
    $$
    f_{i}=max(max_{0le j<i}f_{j}+{i-j+1choose 2}-(sum_{i}-sum_{j}),f_{i-1})
    $$
    (其中$sum_{i}$为$a_{i}$的前缀和,即$sum_{i}=sum_{i-1}+a_{i}$)

    另外,转移中允许$j$上被选择,但这显然不如直接从上一个区间左端点转移更优,因此没关系

    将${i-j+1choose 2}$展开,即
    $$
    f_{i}=max({i+1choose 2}-sum_{i}+max_{0le j<i}(f_{j}+{jchoose 2}+sum_{j})-ij,f_{i-1})
    $$

    这个与普通的斜率优化不同,维护的不是凸包(取最小值即维护凸包),具体过程如下:

    这个问题可以看作$forall 0le j<i,y=-jx+(f_{j}+{jchoose 2}+sum_{j})$这些直线在$x=i$时的最大值

    维护一个栈,插入当前直线,若当前直线与栈顶直线交点在栈顶与下一条直线交点右侧,即可弹出栈顶,最终插入当前直线

    对于求$i$上的最大值,也就是二分找到单调栈中相邻两条直线交点(显然这个交点具有单调递增的性质)第一个在$x=i$右侧的位置,取这个两条直线中靠近栈顶的直线即可

    另外由于$i$单调递增,二分其实并不需要,借助单调性就可以找到该位置了

    由此,我们就可以$o(n)$处理出一个前缀和后缀的答案,分别记作$pre_{i}$和$suf_{i}$

    对于一个询问$(x,y)$,如果不选择该位置,答案也就是$pre_{x-1}+suf_{x+1}$,如果选择该位置,枚举该位置所对应的区间,答案为
    $$
    max_{xin [l,r]}pre_{l-1}+suf_{r+1}+{r-l+2choose 2}-(sum_{r}-sum_{l-1}+(y-a_{x}))
    $$
    化简后,可以发现即
    $$
    (pre_{l-1}+frac{l^{2}-3l}{2}+sum_{l-1})+(suf_{r+1}+frac{r^{2}+3r}{2}-sum_{r})-lr+(a_{x}-y+1)
    $$
    第一个和第二个式子分别与$l$和$r$有关,以下记为$A_{l}$和$B_{r}$,若预处理出$ans_{x}=max_{xin [l,r]}A_{l}+B_{r}-lr$,则询问答案即
    $$
    max(ans_{x}+(a_{x}-y+1),pre_{x-1}+suf_{x+1})
    $$
    以下问题即求$ans_{x}$,考虑用$solve(L,R)$表示求出$forall xin [L,R],ans'_{x}=max_{xin [l,r]subseteq [L,R]}A_{l}+B_{r}-lr$,那么所要执行的也就是$solve(1,n)$

    关于$solve(L,R)$的计算显然是分治,将$[l,r]$是否跨越$mid=lfloorfrac{L+R}{2} floor$分类讨论:

    1.若$[l,r]$未跨越$mid$(即$[l,r]subseteq [L,mid]$或$[l,r]subseteq [mid+1,R]$),递归处理,求出最大值后再与下面这种情况取max即可

    2.若$[l,r]$跨越$mid$,不妨假设$xin [L,mid]$(再另一边类似),$forall lin [L,mid]$去求出$rin [mid+1,R]$的最大值,那么前缀最大之就是答案

    当确定$l$后,后者也就是$A_{l}+max_{rin [mid+1,R]}-rl+B_{r}$,也就是若干条直线在$l$上的最大值,用与之前一样的方式用单调栈预处理即可

    同样可以利用单调性做到线性,因此总复杂度即分治复杂度,为$o(nlog n)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define N 300005
     4 #define ll long long
     5 struct line{
     6     int k;
     7     ll b;
     8     ll get(int x){
     9         return 1LL*k*x+b;
    10     }
    11 }st[N];
    12 int n,m,x,y,a[N];
    13 ll sum[N],pre[N],suf[N],A[N],B[N],c[N],ans[N];
    14 double get_cross(line x,line y){
    15     return -1.0*(x.b-y.b)/(x.k-y.k);
    16 }
    17 void add(line k){
    18     while ((m>1)&&(get_cross(st[m],k)>get_cross(st[m],st[m-1])))m--;
    19     st[++m]=k;
    20 }
    21 void solve(int l,int r){
    22     if (l==r)return;
    23     int mid=(l+r>>1);
    24     solve(l,mid);
    25     solve(mid+1,r);
    26     m=0;
    27     for(int i=mid+1;i<=r;i++)add(line{-i,B[i]});
    28     for(int i=l,pos=m;i<=mid;i++){
    29         while ((pos>1)&&(get_cross(st[pos],st[pos-1])<i))pos--;
    30         c[i]=st[pos].get(i)+A[i];
    31     }
    32     for(int i=l;i<mid;i++)c[i+1]=max(c[i+1],c[i]);
    33     m=0;
    34     for(int i=l;i<=mid;i++)add(line{-i,A[i]});
    35     for(int i=mid+1,pos=m;i<=r;i++){
    36         while ((pos>1)&&(get_cross(st[pos],st[pos-1])<i))pos--;
    37         c[i]=st[pos].get(i)+B[i];
    38     }
    39     for(int i=r;i>mid+1;i--)c[i-1]=max(c[i-1],c[i]);
    40     for(int i=l;i<=r;i++)ans[i]=max(ans[i],c[i]);
    41 }
    42 int main(){
    43     scanf("%d",&n);
    44     for(int i=1;i<=n;i++){
    45         scanf("%d",&a[i]);
    46         sum[i]=sum[i-1]+a[i];
    47     }
    48     add(line{0,0});
    49     int pos=1;
    50     for(int i=1;i<=n;i++){
    51         if (pos+1>=m)pos=m;
    52         while ((pos>1)&&(get_cross(st[pos],st[pos-1])<i))pos--;
    53         pre[i]=max(pre[i-1],st[pos].get(i)+1LL*(i+1)*i/2-sum[i]);
    54         add(line{-i,pre[i]+1LL*i*(i-1)/2+sum[i]});
    55     }
    56     for(int i=n;i;i--)sum[i]=sum[i+1]+a[i];
    57     reverse(sum+1,sum+n+1);
    58     m=0,add(line{0,0}),pos=1;
    59     for(int i=1;i<=n;i++){
    60         if (pos+1>=m)pos=m;
    61         while ((pos>1)&&(get_cross(st[pos],st[pos-1])<i))pos--;
    62         suf[n-i+1]=max(suf[n-i+2],st[pos].get(i)+1LL*(i+1)*i/2-sum[i]);
    63         add(line{-i,suf[n-i+1]+1LL*i*(i-1)/2+sum[i]});
    64     }
    65     for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];
    66     for(int i=1;i<=n;i++)A[i]=pre[i-1]+1LL*i*(i-3)/2+sum[i-1];
    67     for(int i=1;i<=n;i++)B[i]=suf[i+1]+1LL*i*(i+3)/2-sum[i];
    68     for(int i=1;i<=n;i++)ans[i]=A[i]+B[i]-1LL*i*i;
    69     solve(1,n);
    70     scanf("%d",&m);
    71     for(int i=1;i<=m;i++){
    72         scanf("%d%d",&x,&y);
    73         printf("%d
    ",max(pre[x-1]+suf[x+1],ans[x]+(a[x]-y+1)));
    74     }
    75 }
    View Code
  • 相关阅读:
    4. Validator校验器的五大核心组件,一个都不能少
    如何快速提高数据库查询效率
    linux系统简介
    echo命令
    Linux-->基本查找及vim使用
    jmeter.5.4.1
    Shell文本处理三剑客:grep、sed、awk
    Linux 下的dd命令使用详解
    Linux添加硬盘和挂载两个命令fdisk和mount的使用
    Linux系统常用命令速查手册
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14528844.html
Copyright © 2011-2022 走看看