Description
Solution
50分的非常简单,原式可以拆成一个斜率的式子,我们可以用一个单调栈进行斜率优化(为什么是栈而不是队列,可以自行画个图)
考虑100分
对于某一个点的更改,只需要求出必须选这个点的最优答案和不选这个点的最优答案。
不选这个点的答案非常好求,可以倒过来做一遍DP
设
fi
表示1到i的答案,
gi
表示i到N的答案
那么不选的答案就是
fi−1+gi+1
难点在于选。
设
hx
表示选x这个点的答案。
可以得出
hx=max(fi+gj+(i−j)(i−j−1)/2−sum[j−1]+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]));
}
}