JZOJ 5451 Genocide

Cenocide

Description

给出一个长度为 n 的序列a ai 表示选择第 i 个数的代价,设ci表示是否选择第 i 个数,(0为不选, 1 为选择),对于某种选择方案,定义总收益为
这里写图片描述
接下来有m个道具,第 i 个道具能够将aPi修改成 Xi
对于每个道具,询问仅使用这个道具的情况下,最大的总收益为多少。

Data Constraint

这里写图片描述

Solution

考虑 m =1时怎么做。
预处理出 a 的前缀和序列q
fi 表示 1 ~i的数已处理完的最大收益,考虑使用斜率优化。
若由 j 转移到i比由 k 转移到i更优( j < k),设仅跟 j 相关的项的和为Fj( Fj = fj + j(j1)2 + qj ),则可以得出下面的式子: FjFkjk<i
用一个单调栈维护斜率单调递减即可。
由于第 i 个数可以不选择,因而fi要与 fi1 min

现在考虑 m <=3105时怎么做。
同样的,我们设 gj 表示 j ~n的数已处理完的最大收益,用上述同样的方法可以求得。
对于每一个 Pi ,若不选择这一个数,那么答案显然为 fPi1 + gPi+1
hi 表示必须选择 i 时整个序列的最大收益,那么选择Pi这个数的收益即为 hPi + aPi - Xi
现在考虑如何求出 h
对于一个hx hx = max ( fi + gj - qj1 + qi + (ji)(ji1)2 )( i < x < j

令F_i=fi+ i(i+1)2 + qi , Gi = gi + i(i1)2 - qi1
hx = max ( Fi + Gj - ij )( i < x < j

考虑分治,对于分治区间(l , r ),考虑j mid 右边, i mid左边的情况来更新 x 在(l, mid )内的 h
Wi= Fi + max Gj - i *j),那么 hx 每一次都可以由 Wi 更新到( l <i< x ),做个前缀最大即可。

那现在考虑如何求出Wi,可以发现随着 i 的递增,决策点j越来越前,考虑对右区间维护一个斜率单调递减的单调栈(这里就不推式子来证明了,自行手推吧),随着 i 的增加,不断将栈顶元素退掉(当栈顶的斜率<i时便可退掉栈顶,这个自己推一下便可以发现了),如此这般便能求出对于每个 i 的最优决策点了。
仔细观察便能发现这样做并不能让所有的情况更新到hx,将 f g对调后再翻转, a 也翻转,之后再做一遍分治所有的情况便都能更新到每一个hx了。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

#define fo(i,j,l) for(int i=j;i<=l;i++)
#define fd(i,j,l) for(int i=j;i>=l;i--)

using namespace std;
typedef long long ll;
typedef double db;
const ll N=66e4,maxn=1e12;
ll a[N],b[N],f[N],g[N],h[N],t[N],dd[N];
int top,n,m,j,k,l,i,c; 
ll F[N],G[N],d[N],bb;

double xl(ll a,ll b)
{return (F[a]-F[b])/(a-b*1.0);}

double xx(ll a,ll b)
{return (G[a]-G[b])/(a-b*1.0);}

ll max(ll a,ll b)
{return a>b?a:b;}

void ef(int l,int r,int f)
{
    if(l==r)return;
    int k=0,mid=(l+r)/2;
    fo(i,mid+1,r){
        while(k-1&&xx(d[k],i)>=xx(d[k-1],d[k]))k--;
        d[++k]=i;
    }
    t[l-1]=-maxn;
    fo(i,l,mid){
        while(k-1&&xx(d[k-1],d[k])<=i)k--;
        t[i]=max(t[i-1],F[i]+G[d[k]]-i*d[k]);
    }
    if(f==0)fo(i,l,mid)h[i]=max(h[i],t[i-1]);
    else fo(i,l,mid)h[n-i+1]=max(h[n-i+1],t[i-1]);
    ef(l,mid,f); ef(mid+1,r,f);
}

int main()
{
    cin>>n;
    fo(i,1,n)scanf("%lld",&a[i]),b[i]=b[i-1]+a[i];
    int t;
    d[t=1]=0;
    fo(i,1,n){
        while(t-1&&xl(d[t-1],d[t])<i)t--;
        f[i]=max(f[i-1],f[d[t]]+(ll)(i-d[t])*(i-d[t]+1)/2-b[i]+b[d[t]]);
        F[i]=f[i]+(ll)(i-1)*(ll)i/2+b[i];
        while(t-1&&xl(d[t],i)>=xl(d[t-1],d[t]))t--;
        d[++t]=i;
    }
    d[t=1]=0;
    fo(i,1,n)b[i]=b[i-1]+a[n-i+1];
    fo(i,1,n){
        while(t-1&&xl(d[t-1],d[t])<=i)t--;
        g[i]=max(g[i-1],g[d[t]]+(ll)(i-d[t])*(i-d[t]+1)/2-b[i]+b[d[t]]);
        F[i]=g[i]+(ll)(i-1)*(ll)i/2+b[i];
        while(t-1&&xl(d[t],i)>=xl(d[t-1],d[t]))t--;
        d[++t]=i;
    }
    fo(i,1,n/2)swap(g[i],g[n-i+1]);
    fo(i,1,n+1)dd[i]=f[i-1]+g[i+1],b[i]=b[i-1]+a[i];
    fo(i,0,n+1)F[i]=f[i]+(ll)i*(i+1)/2+b[i],G[i]=g[i]+(ll)i*(i-1)/2-b[i-1];
    fo(i,1,n)h[i]=-maxn;
    ef(0,n+1,0);
    fo(i,1,n+1)swap(f[i],g[i]),b[i]=b[i-1]+a[n-i+1];
    fo(i,1,n/2)swap(f[i],f[n-i+1]),swap(g[i],g[n-i+1]);
    fo(i,0,n+1)F[i]=f[i]+(ll)i*(i+1)/2+b[i],G[i]=g[i]+(ll)i*(i-1)/2-b[i-1];
    ef(0,n+1,1); 
    cin>>m;
    fo(i,1,m){
        scanf("%d%lld",&c,&bb);
        ll ans=max(dd[c],h[c]-(bb-a[c]));
        printf("%lld\n",ans);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值