[ARC066F]Contest with Drinks Hard

题意:给定$t_{1\cdots n}$,你要确定一个$01$数组$c_{1\cdots n}$使得$\left(\sum\limits_{1\leq i\leq j\leq n}\prod\limits_{k=i}^jc_k\right)-\sum\limits_{i=1}^nc_it_i$最大,现在有$m$次询问,形如“假如把$t_p$改为$x$,答案是多少?”

相当于选择一些互不相邻的区间,每个长度为$k$的区间有$\frac{k(k+1)}2$的贡献,一个位置$i$被选中则要付出$t_i$的代价

首先看没有询问怎么做,设$f_i$表示前$i$位的答案,$s$为$t$的前缀和,则$f_i=\max\left(f_{i-1},\max\limits_{0\leq j\lt i}f_j+\frac{(i-j)(i-j+1)}2-(s_i-s_j)\right)$

把后面的$\max$里的式子拆开:$\left(-ji+f_j+\frac{j^2-j}2+s_j\right)+\frac{i^2+i}2-s_i$,如果固定$j$,那么括号里是关于$i$的一次函数,整个DP的过程相当于不断地加直线和询问所有直线在$x=i$处的最大值,因为加入的直线斜率越来越小,询问的$x$越来越大,所以用栈维护直线形成的下凸壳即可

现在考虑有询问怎么做,如果一个方案选择的区间不包含$p$,那么问题可以拆成在$[1,p-1],[p+1,n]$中的子问题,把上面的DP正反各做一次得到正的$f_i$和反的$g_i$,这一部分的答案就是$f_{p-1}+g_{p+1}$

如果一个方案选择的区间包含$p$,我们想要求出$h_i$表示选择的某个区间包含$i$的最大答案

最朴素的想法是枚举每一个区间$[l,r]$,用$f_{l-1}+g_{r+1}+\frac{(r-l+1)(r-l+2)}2-\sum\limits_{i=l}^rt_i$更新$h_{l\cdots r}$,这样做太慢,考虑分治

假设当前分治区间为$[l,r]$,我们想要算出所有跨越$[mid,mid+1]$的区间对答案的贡献

枚举右端点$i\in[mid+1,r]$并用$g_{i+1}+\max\limits_{l-1\leq j\lt mid}f_j+\frac{(i-j)(i-j+1)}2-(s_i-s_j)$更新$h_{mid+1\cdots i}$,枚举左端点的左边$i\in[l-1,mid-1]$并用$f_i+\max\limits_{mid\lt j\leq r}g_{j+1}+\frac{(j-i)(j-i+1)}2-(s_j-s_i)$更新$h_{i+1\cdots mid}$,这两个DP可以用和前面一样的方法来完成(先加完直线再单调地查询最大值)

预处理完$f,g,h$后,答案就是$\max(f_{p-1}+g_{p+1},h_p+t_p-x)$

总时间复杂度$O(n\log n)$

扫描二维码关注公众号,回复: 3740854 查看本文章
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef double du;
const ll inf=9223372036854775807ll;
struct line{
	ll k,b;//y=kx+b
	line(ll k=0,ll b=0):k(k),b(b){}
	ll get(ll x){return k*x+b;}
}st[300010],u;
int tp;
du its(line a,line b){
	return(b.b-a.b)/du(a.k-b.k);
}
int t[300010],n;
ll s[300010],f[300010],g[300010];
void dp(ll*f){
	int i;
	for(i=1;i<=n;i++)s[i]=s[i-1]+t[i];
	tp=1;
	st[1]=line(0,0);
	for(i=1;i<=n;i++){
		while(tp>1&&st[tp].get(i)<=st[tp-1].get(i))tp--;
		f[i]=max(st[tp].get(i)+((ll)i*i+i)/2-s[i],f[i-1]);
		u=line(-i,f[i]+((ll)i*i-i)/2+s[i]);
		while(tp>1&&its(u,st[tp-1])>=its(st[tp],st[tp-1]))tp--;
		st[++tp]=u;
	}
}
ll h[300010];
void solve(int l,int r){
	if(l==r){
		h[l]=1ll-t[l];
		return;
	}
	int mid,i,now;
	ll mx;
	mid=(l+r)>>1;
	solve(l,mid);
	solve(mid+1,r);
	tp=0;
	for(i=l-1;i<mid;i++){
		u=line(-i,f[i]+((ll)i*i-i)/2+s[i]);
		while(tp>1&&its(u,st[tp-1])>=its(st[tp],st[tp-1]))tp--;
		st[++tp]=u;
	}
	now=1;
	mx=-inf;
	for(i=r;i>mid;i--){
		while(now<tp&&its(st[now],st[now+1])>=i)now++;
		mx=max(mx,st[now].get(i)+((ll)i*i+i)/2-s[i]+g[i+1]);
		h[i]=max(h[i],mx);
	}
	tp=0;
	for(i=mid+1;i<=r;i++){
		u=line(-i,g[i+1]+((ll)i*i+i)/2-s[i]);
		while(tp>1&&its(u,st[tp-1])>=its(st[tp],st[tp-1]))tp--;
		st[++tp]=u;
	}
	now=tp;
	mx=-inf;
	for(i=l-1;i<mid;i++){
		while(now>1&&its(st[now],st[now-1])<=i)now--;
		mx=max(mx,st[now].get(i)+((ll)i*i-i)/2+s[i]+f[i]);
		h[i+1]=max(h[i+1],mx);
	}
}
int main(){
	int i,m,x,y;
	scanf("%d",&n);
	for(i=1;i<=n;i++)scanf("%d",t+i);
	dp(f);
	reverse(t+1,t+n+1);
	dp(g);
	reverse(t+1,t+n+1);
	reverse(g+1,g+n+1);
	for(i=1;i<=n;i++)s[i]=s[i-1]+t[i];
	solve(1,n);
	scanf("%d",&m);
	while(m--){
		scanf("%d%d",&x,&y);
		printf("%lld\n",max(f[x-1]+g[x+1],h[x]+t[x]-y));
	}
}

猜你喜欢

转载自www.cnblogs.com/jefflyy/p/9858865.html