题解 P4192 旅行规划

Solution

使用分块 , 将每个点的 \(x\) 坐标设为它的下标减去块首下标 \(+1\) , \(y\) 坐标设为在此处下车的旅行价值 (前缀和) .
对于每个块我们计算出它的上凸包 , 用来维护最大值 .
对于每次修改的 \(x,y\) 之后的块 , 直接维护 \(sumtag\) 表示全局增量就可以 .
对于 \(x,y\) 所在的块 , 直接暴力重构 .
同时我们对每个块维护 \(addtag\) 表示这个区间里的美观度的全局增量
那么每个点的旅行价值即为 \(y+x* addtag+sumtag\) , 可以发现最大值所取到的点一定在凸包上 , 并且在 \(addtag\) 增大时向右移动 , 减小时向左移动 , 我们在做区间加时更新即可 . 一些细节见代码 .
复杂度 \(O(n\sqrt n)\)

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
int read()
{
    int ret=0;bool f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=!f;c=getchar();}
    while(c>='0'&&c<='9')ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();
    return f?ret:-ret;
}
const int maxn=1e5+5;
const int maxm=320;
const ll inf=1e18;
int n,m;
struct point
{
    ll x,y;
    const bool operator <(const point &a)const{if(x!=a.x)return x<a.x;return y<a.y;}
    const point operator -(const point &a)const{return point{x-a.x,y-a.y};}
    const ll operator *(const point &a)const{return x*a.y-y*a.x;}
};
ll ori[maxn];
int siz,bl[maxn],bnum;
struct block
{
    point p[maxm],convex[maxm];int top;
    int num,nowmax;
    ll sumtag,addtag;
    void andrew()
    {
        top=1;
        convex[1]=p[1];
        for(int i=2;i<=num;i++)
        {
            while(top>=2&&(convex[top]-convex[top-1])*(p[i]-convex[top])>=0)top--;
            convex[++top]=p[i];
        }
        for(int i=1;i<=top;i++)if(i==top||convex[i].y>=convex[i+1].y){nowmax=i;break;}
    }
    ll calc(point a){return a.y+a.x*addtag+sumtag;}
    void rebuild()
    {
        for(int i=1;i<=num;i++)p[i].y=calc(p[i]);
        sumtag=0;addtag=0;
        andrew();
    }
    void add(int x)
    {
        addtag+=x;
        if(x>0)while(nowmax!=top&&calc(convex[nowmax])<=calc(convex[nowmax+1]))nowmax++;
        else while(nowmax!=1&&calc(convex[nowmax])<=calc(convex[nowmax-1]))nowmax--;
    }
    ll getmax(){return calc(convex[nowmax]);}
}b[maxm];
int main()
{
    n=read();
    siz=sqrt(n);
    for(int i=1;i<=n;i++)bl[i]=(i-1)/siz+1;
    for(int i=1;i<=n;i++)ori[i]=ori[i-1]+read();
    for(int i=1;i<=n;i++)
    {
        b[bl[i]].num++;
        int now=b[bl[i]].num;
        b[bl[i]].p[now].x=now;
        b[bl[i]].p[now].y=ori[i];
    }
    bnum=bl[n];
    for(int i=1;i<=bnum;i++)b[i].rebuild();
    m=read();
    while(m--)
    {
        int type=read();
        if(type==0)
        {
            int x,y,k;x=read();y=read();k=read();
            if(bl[x]==bl[y])
            {
                for(int i=x;i<=y;i++)b[bl[x]].p[i-(bl[x]-1)*siz].y+=(ll)(i-x+1)*k;
                for(int i=y+1;i<=min(n,bl[x]*siz);i++)b[bl[x]].p[i-(bl[x]-1)*siz].y+=(ll)(y-x+1)*k;
                b[bl[x]].rebuild();
                for(int i=bl[y]+1;i<=bnum;i++)b[i].sumtag+=(ll)(y-x+1)*k;
            }
            else 
            {
                for(int i=x;i<=bl[x]*siz;i++)b[bl[x]].p[i-(bl[x]-1)*siz].y+=(ll)(i-x+1)*k;
                for(int i=(bl[y]-1)*siz+1;i<=y;i++)b[bl[y]].p[i-(bl[y]-1)*siz].y+=(ll)k*(i-x+1);
                for(int i=y+1;i<=min(n,bl[y]*siz);i++)b[bl[y]].p[i-(bl[y]-1)*siz].y+=(ll)k*(y-x+1);
                b[bl[x]].rebuild();b[bl[y]].rebuild();
                for(int i=bl[x]+1;i<=bl[y]-1;i++)b[i].add(k),b[i].sumtag+=(ll)((i-1)*siz-x+1)*k;
                for(int i=bl[y]+1;i<=bnum;i++)b[i].sumtag+=(ll)(y-x+1)*k;
            }
        }
        else if(type==1)
        {
            int x,y;x=read();y=read();
            ll ret=-inf;
            if(bl[x]==bl[y])
                for(int i=x;i<=y;i++)ret=max(ret,b[bl[x]].calc(b[bl[x]].p[i-(bl[x]-1)*siz]));
            else 
            {
                for(int i=x;i<=bl[x]*siz;i++)ret=max(ret,b[bl[x]].calc(b[bl[x]].p[i-(bl[x]-1)*siz]));
                for(int i=(bl[y]-1)*siz+1;i<=y;i++)ret=max(ret,b[bl[y]].calc(b[bl[y]].p[i-(bl[y]-1)*siz]));
                for(int i=bl[x]+1;i<=bl[y]-1;i++)ret=max(ret,b[i].getmax());
            }
            printf("%lld\n",ret);
        }
    }
    return 0;
}

posted @ 2021-06-20 19:34  zero4338  阅读(39)  评论(0编辑  收藏  举报