[JZOJ5451]【NOIP2017提高A组冲刺11.4】Genocide

Description

这里写图片描述
这里写图片描述

Solution

50分的非常简单,原式可以拆成一个斜率的式子,我们可以用一个单调栈进行斜率优化(为什么是栈而不是队列,可以自行画个图)

考虑100分
对于某一个点的更改,只需要求出必须选这个点的最优答案和不选这个点的最优答案。

不选这个点的答案非常好求,可以倒过来做一遍DP
fi 表示1到i的答案, gi 表示i到N的答案
那么不选的答案就是 fi1+gi+1

难点在于选。

hx 表示选x这个点的答案。
可以得出 hx=max(fi+gj+(ij)(ij1)/2sum[j1]+sum[i]),i<x<j
其中sum为费用的前缀和

暴力匹配显然超时
考虑一次性将所有的位置的h求出来

CDQ分治!
对于处理区间 [L,R] ,考虑mid左边的i,mid右边所有的j对mid左边的x的贡献。

可以发现mid右边的所有j都可以弄到一个栈里,和之前的斜率优化非常相似(几乎一模一样)

然后左边相应的扫,扫到一个位置就更新它的h,相应的弹栈

对于mid右边的x,做法完全一样,只需要倒过来做,把左边的i扔到栈里,右边倒过来扫
式子推的比较恶心,还要处理变号等一系列问题(我就是受害者

其实可以全部做完以后将序列翻转重新做一次,那么之前的代码可以再次利用,不需要重新推导。
复杂度 O(NlogN)

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 300005
#define LL long long
using namespace std;
int n,m,d[N],l,r;
LL f[N],a[N],sum[N],c[N],g[N],f1[N],g1[N],h[N];
LL xl(LL k)
{
    return f[k]+(k-1)*k/2+sum[k];
}
LL xl2(LL k)
{
    return g[k]+(k+1)*k/2-sum[k-1];
}
void cdq(int l,int r)
{
    if(l==r) return;
    int top=0;
    int mid=(l+r)/2;
    fo(i,mid+1,r) 
    {
        while(top>1&&(g1[i]-g1[d[top]])*(LL)(d[top]-d[top-1])>=(g1[d[top]]-g1[d[top-1]])*(LL)(i-d[top])) top--;
        d[++top]=i;
    }
    LL mx=-1e15;
    fo(i,l,mid-1)
    {
        while(top>1&&(g1[d[top]]-g1[d[top-1]])<=(LL)i*(LL)(d[top]-d[top-1])) top--;
        mx=max(mx,(f1[i]+g1[d[top]])-(LL)i*d[top]);
        h[i+1]=max(h[i+1],mx);
    }
    top=0;
    fod(i,mid,l)
    {
        while(top>1&&(f1[i]-f1[d[top]])*(LL)(d[top]-d[top]-1)<=(f1[d[top]]-f1[d[top-1]])*(LL)(i-d[top])) top--;
        d[++top]=i;
    }
    mx=-1e15;
    fod(i,r,mid+2)
    {
        while(top>1&&(f1[d[top]]-f1[d[top-1]])<=(LL)i*(LL)(d[top]-d[top-1])) top--;
        mx=max(mx,(f1[d[top]]+g1[i])-(LL)i*d[top]);
        h[i-1]=max(h[i-1],mx);
    }
    cdq(l,mid),cdq(mid+1,r);
}
int main()
{
    cin>>n;
    fo(i,1,n) scanf("%lld",&a[i]),sum[i]=a[i]+sum[i-1];
    cin>>m;
    l=0,r=0;
    fo(i,1,n)
    {
        while(l<r&&xl(d[r])-xl(d[r-1])<=(LL)i*(LL)(d[r]-d[r-1])) r--;
        f[i]=max(f[i-1],f[d[r]]+(LL)(i-d[r])*(LL)(i-d[r]+1)/2-sum[i]+sum[d[r]]);
        while(l<r&&(xl(i)-xl(d[r]))*(LL)(d[r]-d[r-1])>=(xl(d[r])-xl(d[r-1]))*(LL)(i-d[r])) r--;
        d[++r]=i;
        f1[i]=(f[i]+(LL)i*(i+1)/2+sum[i]);
    }
    f1[0]=0;
    l=0,r=0;
    d[0]=n+1;
    memset(h,128,sizeof(h));
    fod(i,n,1)
    {
        while(l<r&&xl2(d[r])-xl2(d[r-1])<=(LL)i*(LL)(d[r]-d[r-1])) r--;
        g[i]=max(g[i+1],g[d[r]]+(LL)(d[r]-i)*(LL)(d[r]-i+1)/2+sum[i-1]-sum[d[r]-1]);
        while(l<r&&(xl2(i)-xl2(d[r]))*(LL)(d[r]-d[r-1])<=(xl2(d[r])-xl2(d[r-1]))*(LL)(i-d[r])) r--;
        d[++r]=i;
        g1[i]=(g[i]+(LL)i*(i-1)/2-sum[i-1]);
    }
    g1[n+1]=(LL)(n+1)*(n)/2-sum[n];
    if(f[n]!=g[1]) printf("WA");
    cdq(0,n+1);
    fo(q,1,m)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%lld\n",max(h[x]-(y-a[x]),f[x-1]+g[x+1]));
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值