[ARC066F]Contest with Drinks Hard

Description

给出一个序列a,你需要求出一个0/1序列c,使得

i=1nj=ink=ijCki=1nCiAi

最大
给出m次修改形如(x,y),表示把a[x]改成y,每次修改之间独立,对于每次修改之后求出答案

n,m<=3*1e5

Solution

首先一次询问可以直接Dp
设Fi表示考虑到i的最大价值,O(n)转移显然
发现转移可以斜率优化,不过这个斜率优化写的是单调栈,自己感受一下
正着做一遍再反着做一遍,接下来我们只需要求出hi表示i必须选的答案
这个东西可以用cdq分治做,为了保证覆盖所有情况要正着做一遍再反着做一遍
注意分治边界的一些奇怪东西

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef long long ll;
typedef double db;
int read() {
    char ch;
    for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
    int x=ch-'0';
    for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x;
}

const int N=3*1e5+5;
const ll inf=1e16;

int n,m,x,y,a[N],stack[N],top;
ll f[N],g[N],h[N],ans[N],t[N],c[N],tmp[N],sum[N];

db P(int y,int x) {
    return (t[y]+(ll)y*(y-1)/2+sum[y]-t[x]-(ll)x*(x-1)/2-sum[x])*1.0/(y-x);
}

void Dp() {
    fo(i,1,n) sum[i]=sum[i-1]+a[i];stack[top=1]=0;
    fo(i,1,n) {
        while (top>1&&P(stack[top],stack[top-1])<=i) top--;
        int now=stack[top];
        t[i]=max(t[i-1],t[now]+(ll)(i-now)*(i-now+1)/2+sum[now]-sum[i]);
        while (top>1&&P(stack[top],stack[top-1])<P(i,stack[top])) top--;
        stack[++top]=i;
    }
}

void solve(int l,int r) {
    if (l==r) {
        h[l]=max(h[l],g[r+1]+f[l-1]+1-a[l]);
        return;
    }
    int mid=l+r>>1;
    stack[top=1]=l-1;
    fo(i,l,mid-1) {
        while (top>1&&P(stack[top],stack[top-1])<P(i,stack[top])) top--;
        stack[++top]=i;
    }
    fo(i,mid,r) {
        while (top>1&&P(stack[top],stack[top-1])<=i) top--;
        int now=stack[top];
        tmp[i]=t[now]+(ll)(i-now)*(i-now+1)/2+sum[now]-sum[i]+g[i+1];
    }
    fd(i,r-1,mid) tmp[i]=max(tmp[i],tmp[i+1]);
    fo(i,mid,r) h[i]=max(h[i],tmp[i]);
    solve(l,mid);solve(mid+1,r);
}
int main() {
    freopen("genocide.in","r",stdin);
    freopen("genocide.out","w",stdout);
    n=read();
    fo(i,1,n) a[i]=read();
    Dp();fo(i,1,n) f[i]=t[i];
    fo(i,1,n/2) swap(a[i],a[n-i+1]);
    Dp();fo(i,1,n) g[i]=t[n-i+1];
    fo(i,1,n) ans[i]=-inf;

    fo(i,1,n/2) swap(a[i],a[n-i+1]);
    fo(i,1,n) t[i]=f[i];
    fo(i,1,n) sum[i]=sum[i-1]+a[i];
    fo(i,1,n) h[i]=-inf;
    solve(1,n);
    fo(i,1,n) ans[i]=max(ans[i],h[i]);

    fo(i,1,n) swap(f[i],g[i]);
    fo(i,1,n/2) {
        swap(a[i],a[n-i+1]);
        swap(f[i],f[n-i+1]);
        swap(g[i],g[n-i+1]);
    }
    fo(i,1,n) t[i]=f[i];
    fo(i,1,n) sum[i]=sum[i-1]+a[i];
    fo(i,1,n) h[i]=-inf;
    solve(1,n);
    fo(i,1,n) ans[i]=max(ans[i],h[n-i+1]);

    fo(i,1,n/2) {
        swap(f[i],f[n-i+1]);
        swap(g[i],g[n-i+1]);
        swap(a[i],a[n-i+1]);
    }
    fo(i,1,n) swap(f[i],g[i]);

    for(m=read();m;m--) x=read(),y=read(),printf("%lld\n",max(f[x-1]+g[x+1],ans[x]+a[x]-y));
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值