zoukankan      html  css  js  c++  java
  • BZOJ2388:旅行规划(travel)——分块凸包

    题目

    OIVillage 是一个风景秀美的乡村,为了更好的利用当地的旅游资源,吸引游客,推动经济发展,xkszltl 决定修建了一条铁路将当地 $n$ 个最著名的经典连接起来,让游客可以通过火车从铁路起点( $1$ 号景点)出发,依次游览每个景区。为了更好的评价这条铁路,xkszltl 为每一个景区都赋予了一个美观度,而一条旅行路径的价值就是它所经过的景区的美观度之和。不过,随着天气与季节的变化,某些景点的美观度也会发生变化。

    xkszltl 希望为每位旅客提供最佳的旅行指导,但是由于游客的时间有限,不一定能游览全部景区,然而他们也不希望旅途过于短暂,所以每个游客都希望能在某一个区间内的车站结束旅程,而 xkszltl 的任务就是为他们选择一个终点使得旅行线路的价值最大。可是当地的景点与前来观光的旅客实在是太多了,xkszltl 无法及时完成任务,于是找到了准备虐杀 NOI2019 的你,希望你能帮助他完成这个艰巨的任务。

    【输入格式】

    第一行给出一个整数 $n$,接下来一行给出 $n$ 的景区的初始美观度。

    第三行给出一个整数 $m$,接下来 $m$ 行每行为一条指令:

    $1.~~~0~x~y~k$:表示将 $x$ 到 $y$ 这段铁路边上的景区的美观度加上 $k$;

    $2.~~~1~x~y$:表示有一名旅客想要在 $x$ 到 $y$ 这段(含 $x$ 与 $y$ )中的某一站下车,你需要告诉他最大的旅行价值。

    【输出格式】

    对于每个询问,输出一个整数表示最大的旅行价值。

    【样例输入】

    5

    1 8 -8 3 -7

    3

    1 1 5

    0 1 3 6

    1 2 4

    【样例输出】

    9

    22

    【数据范围与提示】

    对于 $20\%$ 的数据,$n,m≤3000$;

    对于 $40\%$ 的数据,$n,m≤30000$;

    对于 $50\%$ 的数据,$n,m≤50000$;

    另外 $20\%$ 的数据,$n,m≤100000$,修改操作 $≤20$;

    对于 $100\%$ 的数据,$n,m≤100000$。

    题解

    无力吐槽这组超级强(孱弱)的样例(过了样例还是调出了 INF 个错)

    题目求得是区间最大前缀和,带区间加值操作

    然后发现线段树无法在 $ O(log n) $ 的时间内维护区间加上一次函数后的最大值

    考虑分块维护最大值

    记 $ sum(i) $ 表示前缀和,$ num(i) $ 表示块 $ i $ 加上的一次函数,$ d(i) $ 表示这个块需要加上的常数,

    那么 $ sum(i)=num(i) imes i +d(i) $

    发现最大值形如 $ y=kx+b $,然后就可以在块上维护一个凸包

    当块上加一个一次函数时,凸包的点是不会改变的,那么每次加值时就只要每次重构左右两边的两个凸包即可

    查询时只要在每一个块的凸包上二分即可

    时间:修改时$ O(sqrt{n}) $,查询时 $ O(sqrt{n}) $,所以总时间 $ O(msqrt{n}log n) $

    代码

     1 #include<bits/stdc++.h>
     2 #define LL long long
     3 #define db double
     4 #define _(d) while(d(isdigit(ch=getchar())))
     5 using namespace std;
     6 int R(){
     7     int x;bool f=1;char ch;_(!)if(ch=='-')f=0;x=ch^48;
     8     _()x=(x<<3)+(x<<1)+(ch^48);return f?x:-x;}
     9 const int N=1e5+5,M=1002;
    10 int n,m,a[N],Sz,Mx,p[N],sz[M],tb[M][M];
    11 LL w[N],num[N],f[N],d[N];
    12 db getK(int i,int j){return (db)(w[i]-w[j])/(db)(i-j);}
    13 void update(int x){
    14     int l=(x-1)*Sz+1,r=min(x*Sz,n),tot=0;
    15     p[++tot]=l;
    16     for(int i=l+1;i<=r;i++){
    17         while(tot>1&&getK(p[tot],p[tot-1])<getK(p[tot-1],i))
    18             tot--;
    19         p[++tot]=i;
    20     }
    21     sz[x]=tot;
    22     for(int i=1;i<=tot;i++)tb[x][i]=p[i];
    23     return;
    24 }
    25 void pushdown(int x){
    26     LL o=num[x];
    27     for(int i=(x-1)*Sz+1;i<=min(x*Sz,n);i++)
    28         o+=d[x],w[i]+=o;
    29     num[x]=d[x]=0;
    30     return;
    31 }
    32 void change(int l,int r,int k){
    33     int x=a[l],y=a[r];LL o=0;
    34     pushdown(x);
    35     for(int i=l;i<=min(x*Sz,r);i++)
    36         o+=k,w[i]+=o;
    37     update(x);
    38     for(int i=x+1;i<y;i++)
    39         num[i]+=o,d[i]+=k,o+=1ll*Sz*k;
    40     if(x!=y){
    41         pushdown(y);
    42         for(int i=(y-1)*Sz+1;i<=r;i++)
    43             o+=k,w[i]+=o;
    44     }
    45     o=(r-l+1)*k;
    46     for(int i=r+1;i<=min(n,y*Sz);i++)w[i]+=o;
    47     update(y);
    48     for(int i=y+1;i<=Mx;i++)num[i]+=o;
    49     return;
    50 }
    51 LL find(int i){
    52     if(!i||i>n)return (LL)-2e18;
    53     return (w[i]+num[a[i]]+d[a[i]]*(i-(a[i]-1)*Sz));
    54 }
    55 LL work(int x){
    56     int l=1,r=sz[x];
    57     while(l<=r){
    58         int mid=(l+r)>>1;
    59         LL t1=find(tb[x][mid-1]);
    60         LL t2=find(tb[x][mid]);
    61         LL t3=find(tb[x][mid+1]);
    62         if (t1<t2 && t2<t3) l=mid+1;
    63         else if (t1>t2 && t2>t3) r=mid-1;
    64         else return t2;
    65     }
    66     return l;
    67 }
    68 LL query(int l,int r){
    69     int x=a[l],y=a[r];LL ans=-2e18;
    70     for(int i=x+1;i<y;i++)
    71         ans=max(ans,work(i));
    72     for(int i=l;i<=min(x*Sz,r);i++)
    73         ans=max(ans,find(i));
    74     if(x!=y)
    75         for(int i=(y-1)*Sz+1;i<=r;i++)
    76             ans=max(ans,find(i));
    77     return ans;
    78 }
    79 int main(){
    80     n=R(),Sz=sqrt(n);
    81     for(int i=1;i<=n;i++)
    82         w[i]=w[i-1]+R(),a[i]=(i-1)/Sz+1;
    83     Mx=a[n],m=R();
    84     for(int i=1;i<=Mx;i++)update(i);
    85     for(int i=1;i<=m;i++){
    86         int op=R(),x=R(),y=R(),k;
    87         if(op==1)printf("%lld
    ",query(x,y));
    88         if(!op)k=R(),change(x,y,k);
    89     }
    90     return 0;
    91 }
    View Code
  • 相关阅读:
    移动互联网实战--Apple的APNS桩推送服务的实现(1)
    移动互联网实战--社交游戏的排行榜设计和实现(2)
    移动互联网实战--社交游戏的排行榜设计和实现(1)
    Linux 线程--那一年, 我们一起忽视的pthread_join
    移动互联网实战--资源类APP的数据存储处理和优化
    Thrift 个人实战--RPC服务的发布订阅实现(基于Zookeeper服务)
    移动互联网实战--Web Restful API设计和基础架构
    Thrift 个人实战--Thrift RPC服务框架日志的优化
    Thrift 个人实战--Thrift 服务化 Client的改造
    Kafka集群副本分配算法解析
  • 原文地址:https://www.cnblogs.com/chmwt/p/10579511.html
Copyright © 2011-2022 走看看