Decsription
有一个长度为n的数列,对于每个位置,你可以选/不选,选第i个位置的代价为a[i]
假设c[i]=0/1表示第i个位置选/不选,那么最后的收益定义为:
(∑ni=1∑nj=i(∏jk=ick))−(∑ni=1ciai)
接下来有m个询问,形如x y,表示将a[x]改变成y后的最大收益。改变无后效性。
Analysis
先不考虑询问,如果直接求最大收益怎么做?
显然可以设f[i]表示做到第i个位置的最大收益,然后发现dp可以斜率优化,这样就O(n)了
考虑询问,如果a[x]变成了y,如果不选a[x],答案肯定没有影响
我们只要从头,从尾各做一次dp,那么不选的答案就是f[x-1]+g[x+1]
如果必定要选,考虑求出h[x]表示必选x位置的答案,那么选的答案就是h[x]-a[x]+y了
可以发现
h[x]=maxi≤x≤j(f[i−1]+g[j+1]+(j−i+1)(j−i+2)/2−(sj−si−1))
这个和上面dp很像,由于有i,j两个变量,考虑固定i,求出最优的j
自己手动拆一下,转移可以写成形如
h[x]=max(Fi+Gj−ij)
这时候可以套用上面的斜率优化
但是我们不可能同时枚举i,j,那么考虑分治,每次对于分治区间[l,r],处理l<=i<=mid<=j<=r的i,j,考虑枚举
i
<script type="math/tex" id="MathJax-Element-176">i</script>,预处理出可能成为最优决策的j,那么随着i右移,j必然左移,更新此时的h[i],这样能更新[l,mid]的h[x]
然后反过来做一次,更新[mid+1,r]的h[x]
本题可以O(nlogn)解决
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define fd(i,a,b) for(ll i=a;i>=b;i--)
#define efo(i,v,u) for(int i=last[v],u=to[last[v]];i;i=next[i],u=to[i])
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define mset(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
void read(ll &n)
{
ll t=0,p=1;char ch;
for(ch=getchar();!('0'<=ch && ch<='9');ch=getchar())
if(ch=='-') p=-1;
for(;'0'<=ch && ch<='9';ch=getchar()) t=t*10+ch-'0';
n=t*p;
}
const int N=6e5+5;
int n,sig,top;
ll a[N],s[N],f[N],g[N],h[N],F[N],G[N],sta[N];
void rev(ll *arr)
{
fo(i,1,n/2) swap(arr[i],arr[n-i+1]);
fo(i,1,n) s[i]=s[i-1]+a[i];
}
ll H(ll j){return 2*(sig==0?f[j]:g[j])+2*s[j]+j*j-j;}
db xl(int j,int k){return 1.0*(H(j)-H(k))/(j-k);}
ll S(ll x){return x*(x+1)/2;}
ll dp(ll *d) // j as a point
{
mset(d,0);mset(sta,0);
sta[top=1]=0;
fo(i,1,n)
{
while(top && xl(sta[top-1],sta[top])<=2*i) top--;
int j=sta[top];
d[i]=max(d[i-1],d[j]+S(i-j)-s[i]+s[j]);
while(top && xl(sta[top-1],sta[top])<=xl(sta[top],i)) top--;
sta[++top]=i;
}
}
db cross(ll k1,ll b1,ll k2,ll b2)
{
return 1.0*(b2-b1)/(k1-k2);
}
void solve(int l,int r)
{
if(l==r)
{
h[l]=max(h[l],f[l-1]+g[l+1]+1-a[l]);
return;
}
int mid=l+r>>1;
fo(i,l,mid) F[i]=f[i-1]+s[i-1]+((ll)i*i-3*i)/2+1;
fo(j,mid+1,r) G[j]=g[j+1]-s[j]+((ll)j*j+3*j)/2;
top=0;
//j as a line
fd(j,r,mid+1)
{
while(top && G[j]>=G[sta[top]]) top--;
while(top>1 && cross(-j,G[j],-sta[top],G[sta[top]])<=cross(-sta[top],G[sta[top]],-sta[top-1],G[sta[top-1]]))
top--;
sta[++top]=j;
}
int p=1;ll t=-1e16;
fo(i,l,mid)
{
while(p<top && G[sta[p]]-sta[p]*i<=G[sta[p+1]]-sta[p+1]*i) p++;
t=max(t,F[i]+G[sta[p]]-i*sta[p]);
h[i]=max(h[i],t);
}
solve(l,mid);solve(mid+1,r);
}
int main()
{
scanf("%d",&n);
fo(i,1,n) read(a[i]),s[i]=s[i-1]+a[i];
sig=0;dp(f);
rev(a);sig=1;dp(g);rev(g);rev(a);
mset(h,128);
rev(a);
rev(f);rev(g);swap(f,g);rev(h);
solve(1,n);
rev(h);swap(f,g);rev(g);rev(f);
rev(a);
solve(1,n);
int Q,x,y;
scanf("%d",&Q);
fo(i,1,Q)
{
scanf("%d %d",&x,&y);
printf("%lld\n",max(f[x-1]+g[x+1],h[x]+a[x]-y));
}
return 0;
}