CF455E. Function
感觉不难啊,为什么是 *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;
}