您的位置:首页 > 其它

BZOJ2388: 旅行规划

2015-08-11 09:33 316 查看
http://www.lydsy.com/JudgeOnline/problem.php?id=2388

  带区间加修改,求区间内的最大前缀和。

  设sum[i]代表i的前缀和,tag[i][j]代表i到j位置上每个数字都加上了tag[i][j],add[i][j]代表i到j位置上每个位置的前缀和都加上了add[i][j]。

  对于区间[l,r],设最大答案为ans,ans=max{add[l][r]+tag[l][r]×(i-l+1)+sum[i]|l≤i≤r}。

  设x=(i-l+1),y=sum[i],则有ans=add[l][r]+tag[l][r]×x+y,可以化简为y=-tag[l][r]×x+ans-add[l][r],就是给定一些点,要最大化过某个点且斜率给定的直线的截距,维护上凸壳即可。

  所以我们分块,对于每一块维护上凸壳,查询操作O(√nlog(n)),修改O(√n)。

  注意重建某一块的上凸壳之前要标记下传。

#include<bits/stdc++.h>
const int maxn=100015,maxb=415;
typedef long long int64;
using namespace std;
int n,m,num,siz,idx[maxn];
int64 sum[maxn];
struct Tblock{
static const int maxsiz=415;
static const double eps=1e-9;
int l,r,top;int64 add,tag;
struct Tpoint{int64 x,y;}stk[maxsiz];
void modify(int64 _add,int64 _tag){add+=_add;tag+=_tag;}
double slope(Tpoint a,Tpoint b){return 1.0*(b.y-a.y)/(b.x-a.x);}
void push_down(){for (int i=l;i<=r;++i) sum[i]+=tag*(i-l+1)+add;tag=add=0;}
void rebuild(){
top=0;
for (int i=l;i<=r;++i){
Tpoint cur=(Tpoint){i-l+1,sum[i]};
while (top>1&&slope(stk[top-1],stk[top])+eps<slope(stk[top],cur)) --top;
stk[++top]=cur;
}
stk[0]=(Tpoint){stk[1].x-1,(int64)-1e18};
stk[top+1]=(Tpoint){stk[top].x+1,(int64)-1e18};
}
int64 query(){
double k=-tag;int l=1,r=top;
while (1){
int mid=(l+r)>>1;
double lk=slope(stk[mid-1],stk[mid]),rk=slope(stk[mid],stk[mid+1]);
if (lk+eps>=k&&rk<=eps+k) return stk[mid].y+tag*stk[mid].x+add;
else if (rk>eps+k) l=mid+1;else r=mid-1;
}
}
}block[maxb];
void init_block(){
siz=sqrt(n);
for (int j,i=1;i<=n;i=j){
for (++num,j=i;j-i+1<=siz&&j<=n;++j) idx[j]=num;
block[num].l=i;block[num].r=j-1;block[num].rebuild();
}
}
void init(){
scanf("%d",&n);
for (int i=1;i<=n;++i){scanf("%lld",&sum[i]);sum[i]+=sum[i-1];}
init_block();
}
void modify(int l,int r,int64 v){
if (idx[l]==idx[r]){
block[idx[l]].push_down();
for (int i=l;i<=r;++i) sum[i]+=v*(i-l+1);
for (int i=r+1;i<=block[idx[r]].r;++i) sum[i]+=v*(r-l+1);
block[idx[l]].rebuild();
for (int i=idx[l]+1;i<=num;++i) block[i].modify(v*(r-l+1),0);
}
else{
block[idx[l]].push_down();block[idx[r]].push_down();
for (int i=l;i<=block[idx[l]].r;++i) sum[i]+=v*(i-l+1);
for (int i=block[idx[r]].l;i<=r;++i) sum[i]+=v*(i-l+1);
for (int i=r+1;i<=block[idx[r]].r;++i) sum[i]+=v*(r-l+1);
block[idx[l]].rebuild();block[idx[r]].rebuild();
for (int i=idx[l]+1;i<=idx[r]-1;++i) block[i].modify(v*(block[idx[l]].r-l+1+siz*(i-idx[l]-1)),v);
for (int i=idx[r]+1;i<=num;++i) block[i].modify(v*(r-l+1),0);
}
}
int64 query(int l,int r){
int64 res=-1e18;
if (idx[l]==idx[r])
for (int i=l;i<=r;++i) res=max(res,sum[i]+block[idx[i]].add+block[idx[i]].tag*(i-block[idx[i]].l+1));
else{
for (int i=l;i<=block[idx[l]].r;++i) res=max(res,sum[i]+block[idx[i]].add+block[idx[i]].tag*(i-block[idx[i]].l+1));
for (int i=block[idx[r]].l;i<=r;++i) res=max(res,sum[i]+block[idx[i]].add+block[idx[i]].tag*(i-block[idx[i]].l+1));
for (int i=idx[l]+1;i<=idx[r]-1;++i) res=max(res,block[i].query());
}
return res;
}
void work(){
scanf("%d",&m);
for (int i=1;i<=m;++i){
int t,l,r;int64 v;
scanf("%d%d%d",&t,&l,&r);
switch (t){
case 0:scanf("%lld",&v);modify(l,r,v);break;
case 1:printf("%lld\n",query(l,r));break;
}
}
}
int main(){
init();
work();
return 0;
}


my code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: