bzoj2388 旅行规划

凸包好题

我一开始想的是线段树或平衡树维护最大前缀和,但是区间修改很恶心,后来想分块,发现貌似可以做,修改的话,中间的块打标记,两边的暴力重构,查询的话就是整块二分斜率为零的地方,边上的暴力查询。$O(nsqrt(n)log(n))$

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <iostream>
  4 #include <algorithm>
  5 #include <cmath>
  6 #include <vector>
  7 #define N 100050
  8 #define inf 0x7fffffffffffffff
  9 #define int long long
 10 using namespace std;
 11 struct point{
 12     int x[2];
 13     point(){}
 14     point(int a,int b){x[0]=a,x[1]=b;}
 15     int operator * (point a){return x[0]*a.x[1]-x[1]*a.x[0];}
 16     point operator + (point a){return point(x[0]+a.x[0],x[1]+a.x[1]);}
 17     point operator - (point a){return point(x[0]-a.x[0],x[1]-a.x[1]);}
 18     bool operator < (const point & a)const{
 19         if(x[0]==a.x[0])return x[1]<a.x[1];
 20         return x[0]<a.x[0];
 21     }
 22 }p[N],K;
 23 vector <int> v[1005];
 24 int be[N],en[N],n,nn,m;
 25 int s[N],r[N];
 26 void init(){
 27     for(int i=1;i<=be[n];i++){
 28         for(int j=en[i-1]+1,sz;j<=en[i];j++){
 29             sz=v[i].size();
 30             while(sz>1&&(p[v[i][sz-1]]-p[v[i][sz-2]])*(p[j]-p[v[i][sz-1]])>=0)sz--,v[i].pop_back();
 31             v[i].push_back(j);
 32         }
 33     }
 34 }
 35 void work(int x){
 36     v[x].clear();
 37     for(int i=en[x-1]+1,sz;i<=en[x];i++){
 38         sz=v[x].size();
 39         while(sz>1&&(p[v[x][sz-1]]-p[v[x][sz-2]])*(p[i]-p[v[x][sz-1]])>=0)sz--,v[x].pop_back();
 40         v[x].push_back(i);
 41     }
 42 }
 43 void update(int x,int l,int r,int val){
 44     if(!s[x]&&!::r[x]&&!val)return ;
 45     for(int i=en[x-1]+1;i<=en[x];i++){
 46         p[i].x[1]+=s[x]+(i-en[x-1]-1)*::r[x];
 47         if(i>=l&&i<=r)p[i].x[1]+=(i-l+1)*val;
 48         if(i>r)p[i].x[1]+=(r-l+1)*val;
 49     }
 50     s[x]=::r[x]=0;
 51     work(x);
 52 }
 53 int query(int x){
 54     int l=1,r=v[x].size()-1,mid,ans=0;
 55     point P=point(1,0);
 56     while(l<=r){
 57         mid=(l+r)>>1;
 58         point now=p[v[x][mid]];now.x[1]+=s[x]+::r[x]*(v[x][mid]-en[x-1]-1);
 59         point last=p[v[x][mid-1]];last.x[1]+=s[x]+::r[x]*(v[x][mid-1]-en[x-1]-1);
 60         if((now-last)*P<=0)ans=mid,l=mid+1;
 61         else r=mid-1;
 62     }
 63     return p[v[x][ans]].x[1]+s[x]+::r[x]*(v[x][ans]-en[x-1]-1);
 64 }
 65 signed main(){
 66     scanf("%lld",&n);nn=sqrt(n);
 67     p[0]=point(0,0);
 68     K.x[0]=1;
 69     for(int i=1;i<=n;i++){
 70         scanf("%lld",&K.x[1]);
 71         p[i]=p[i-1]+K;
 72         be[i]=(i-1)/nn+1;
 73     }
 74     for(int i=1;i<=be[n];i++)en[i]=min(i*nn,n);
 75     init();
 76     scanf("%lld",&m);
 77     int o,x,y,z,ans;
 78     while(m--){
 79         scanf("%lld",&o);
 80         if(o==0){
 81             scanf("%lld%lld%lld",&x,&y,&z);
 82             update(be[x],x,y,z);
 83             if(be[x]!=be[y])update(be[y],x,y,z);
 84             for(int i=be[x]+1;i<be[y];i++){
 85                 s[i]+=(en[i-1]+1-x+1)*z;
 86                 r[i]+=z;
 87             }
 88             for(int i=be[y]+1;i<=be[n];i++)
 89                 s[i]+=(y-x+1)*z;
 90         }
 91         else{
 92             ans=-inf;
 93             scanf("%lld%lld",&x,&y);
 94             for(int i=x;i<=min(en[be[x]],y);i++)
 95                 ans=max(ans,p[i].x[1]+s[be[x]]+r[be[x]]*(i-en[be[x]-1]-1));
 96             if(be[x]!=be[y])
 97                 for(int i=en[be[y]-1]+1;i<=y;i++)
 98                     ans=max(ans,p[i].x[1]+s[be[y]]+r[be[y]]*(i-en[be[y]-1]-1));
 99             for(int i=be[x]+1;i<be[y];i++)
100                 ans=max(ans,query(i));
101             printf("%lld\n",ans);
102         }
103     }
104     return 0;
105 }
View Code

 

转载于:https://www.cnblogs.com/Ren-Ivan/p/8458220.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值