CF455E. Function

发布时间 2023-06-19 10:06:37作者: xx019

感觉不难啊,为什么是 *2900 捏。

发现这个玩意的本质是最初在 r,每次不动或向左移动一步,进行 l 次操作,求每次停留的格子权值之和的最小值。显然我们只会停留在至多一个格子上,假设停留在 \(i\),那么权值之和就是 \(\left(l-r+i\right)a_i+\sum\limits_{j=i+1}^ra_j\),且 \(i\in[r-l+1,r]\)

小推一下柿子:\(\left(l-r+i\right)a_i+\sum\limits_{j=i+1}^ra_j=\left[\left(l-r\right)a_i+\left(i\cdot a_i+\sum\limits_{j=i+1}^na_j\right)\right]-\sum\limits_{j=r+1}^na_j\)。发现中括号外的是定值,中括号里是一个形如 \(kx+b\) 这样的直线。我们考虑用李超线段树来求最小值。

但是 \(i\) 的范围限制怎么办?因为李超是不支持删除的,我们联想到回滚莫队的处理方式。假设我们现在正在处理左端点在 \([L_i,R_i]\) 范围内的询问,查询区间为 \([l_j,r_j]\),那么在 \([l_j,R_i]\) 范围内的直线我们可以暴力求出结果比较,在 \([R_i+1,r_j]\) 范围内的直线我们用李超树解决。时间复杂度 \(O(nB+\left(\dfrac{n}{B}\right)^2\log n)\),其中 \(B\) 是快长,平衡后可以做到 \(O(n\sqrt{n\log n})\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define db double
using namespace std;
const int inf=1e18,eps=0;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct seg{
	int k,b;
	int calc(int x){
		return k*x+b;
	}
}s[100005];
db cross(int i,int j){
	return 1.0*(s[j].b-s[i].b)/(s[i].k-s[j].k);
}
int chmin(int i,int j,int x){
	int vi=s[i].calc(x),vj=s[j].calc(x);
	if(abs(vi-vj)>eps)return ((vi<vj)?i:j);
	return min(i,j);
}
struct segtree{
	#define ls p<<1
	#define rs p<<1|1
	#define lson l,mid,ls
	#define rson mid+1,r,rs
	struct Node{
		int flag,id;
		Node():flag(0),id(0){}
	}c[800015];
	void build(int l,int r,int p){
		c[p].flag=0,c[p].id=0;
		if(l==r)return;
		int mid=(l+r)>>1;
		build(lson),build(rson);
	}
	void update(int l,int r,int p,int id){
		if(c[p].flag==0){c[p].flag=1;c[p].id=id;return;}
		int& ID=c[p].id;
		if(l==r){
			if(s[ID].calc(l)>s[id].calc(l))ID=id;
			return;
		}
		int l1=s[ID].calc(l),r1=s[ID].calc(r);
		int l2=s[id].calc(l),r2=s[id].calc(r);
		if(l1<=l2&&r1<=r2)return;
		if(l1>l2&&r1>r2){ID=id;return;}
		int mid=(l+r)>>1;db pos=cross(ID,id);
		if(pos<=mid){
			if(l1<l2)swap(ID,id);
			update(lson,id);
		}
		else{
			if(r1<r2)swap(ID,id);
			update(rson,id);
		}
	}
	void insert(int l,int r,int p,int L,int R,int id){
		if(L<=l&&r<=R){
			update(l,r,p,id);
			return;
		}
		int mid=(l+r)>>1;
		if(L<=mid)insert(lson,L,R,id);
		if(R>mid)insert(rson,L,R,id); 
	}
	int query(int l,int r,int p,int x){
		if(l==r)return c[p].id;
		int mid=(l+r)>>1,id;int ID=c[p].id;
		if(x<=mid)id=query(lson,x);
		else id=query(rson,x);
		return chmin(ID,id,x);
	}
	#undef ls
	#undef rs
	#undef lson
	#undef rson
}Tr;
int ask(int l,int r,int x){
	int res=inf;
	for(int i=l;i<=r;i++)res=min(res,s[i].k*x+s[i].b);
	return res;
}
int bel[100005],bl[505],br[505];
struct Que{
	int l,r,id,ql,qr;
}q[100005];
int cmp(Que x,Que y){
	if(bel[x.ql]^bel[y.ql])return bel[x.ql]<bel[y.ql];
	return x.r<y.r;
}
int a[100005],suf[100005],ans[100005];
signed main(){
	int n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	suf[n+1]=0;
	for(int i=n;i>=1;i--){
		suf[i]=suf[i+1]+a[i];
	}
	s[0]=(seg){0,inf};
	for(int i=1;i<=n;i++){
		s[i]=(seg){a[i],i*a[i]+suf[i+1]};
	}
	int siz=(int)sqrt(n*(int)log2(n)),num=(n+siz-1)/siz;
	for(int i=1;i<=n;i++){
		bel[i]=(i-1)/siz+1;
	}
	for(int i=1;i<=num;i++){
		bl[i]=(i-1)*siz+1,br[i]=i*siz;
	}
	br[num]=n;
	int m=read();
	for(int i=1,l,r;i<=m;i++){
		l=read(),r=read();
		q[i]=(Que){l,r,i,r-l+1,r};
	}
	sort(q+1,q+m+1,cmp);
	for(int i=1;i<=m;i++){
		if(bel[q[i].ql]==bel[q[i].qr]){
			ans[q[i].id]=ask(q[i].ql,q[i].qr,q[i].l-q[i].r)-suf[q[i].r+1];
		}
	}
	for(int i=1,j=1;i<=num;i++){
		int L=br[i]+1,R=br[i];
		Tr.build(-n,n,1);
		while(j<=m){
			int l=q[j].l,r=q[j].r,ql=q[j].ql,qr=q[j].qr,id=q[j].id;
			if(bel[ql]!=i)break;
			if(bel[qr]==i){j++;continue;}
			while(R<qr)R++,Tr.insert(-n,n,1,-n,n,R);
			ans[id]=min(ask(ql,br[i],l-r),s[Tr.query(-n,n,1,l-r)].calc(l-r))-suf[r+1];
			j++;
		}
	}
	for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
	return 0;
}