BZOJ 2388: 旅行规划 分块 单调栈 前缀和

title

BZOJ 2388

LUOGU 4192

Description

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

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

Input

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

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

  1. 0 x y k:表示将 \(x\)\(y\) 这段铁路边上的景区的美观度加上 \(k\)
  2. 1 x y:表示有一名旅客想要在 \(x\)\(y\) 这段(含 \(x\)\(y\) )中的某一站下车,你需要告诉他最大的旅行价值。

Output

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

Sample Input

5
1 8 -8 3 -7
3
1 1 5
0 1 3 6
1 2 4

Sample Output

9
22

HINT

Data Limit:

对于 \(20\%\) 的数据,\(n,m\leqslant 3000\)

对于 \(40\%\) 的数据,\(n,m\leqslant 30000\)

对于 \(50\%\) 的数据,\(n,m\leqslant 50000\)

另外 \(20\%\) 的数据,\(n,m\leqslant 100000\),修改操作 \(\leqslant 20\)

对于 \(100\%\) 的数据,\(n,m\leqslant 100000\)

analysis

这个关于分块的写法,参考 hzwer

此题可以分块。

对于一块内维护一个凸壳,查询每一块内可以二分。

修改头尾暴力重构,如果将一整块加上一个值,那么最优的点是不会变的,因为相当于将每两点间的斜率加上一个定值。


我的一点点想法,我到现在还是认为 \(BIT\) 可以写这道题,毕竟维护凸包的操作我已经会写了,下面就是得想想怎么样把 \(BIT\) 实现一下,可能会咕掉。

code

#include<bits/stdc++.h>

typedef long long ll;
const int maxn=1e5+10,maxm=350;
const ll INF=1ll<<50;

namespace IO
{
    char buf[1<<15],*fs,*ft;
    inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1, ch=getchar();
        while (!isdigit(ch) && ch^'-') ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }

    char Out[1<<24],*fe=Out;
    inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
    template<typename T>inline void write(T x,char str)
    {
        if (!x) *fe++=48;
        if (x<0) *fe++='-', x=-x;
        T num=0, ch[20];
        while (x) ch[++num]=x%10+48, x/=10;
        while (num) *fe++=ch[num--];
        *fe++=str;
    }
}

using IO::read;
using IO::write;

template<typename T>inline bool chkMin(T &a,const T &b) { return a>b ? (a=b, true) : false; }
template<typename T>inline bool chkMax(T &a,const T &b) { return a<b ? (a=b, true) : false; }
template<typename T>inline T min(T a,T b) { return a<b ? a : b; }
template<typename T>inline T max(T a,T b) { return a>b ? a : b; }

ll h[maxn],fir[maxm],dif[maxm],add[maxm];//首项、公差
inline double slope(int x,int y)
{
    return (double)(h[y]-h[x])/(y-x);
}

int bel[maxn],n;
int st[maxm],ed[maxm],num[maxn];
inline ll cal(int x)
{
    if (!x || x==n+1) return -INF;
    int t=bel[x];
    return h[x]+fir[t]+dif[t]*(x-st[t])+add[t];
}

int Stack[maxm],top;
int con[maxm][maxm];//凸包
inline void build(int x)
{
    top=0;
    Stack[++top]=st[x];
    for (int i=st[x]+1; i<=ed[x]; ++i)
    {
        while (top>1 && slope(Stack[top-1],Stack[top])<slope(Stack[top-1],i)) --top;
        Stack[++top]=i;
    }
    Stack[0]=0, Stack[top+1]=n+1;
    num[x]=top;
    for (int i=0; i<=top+1; ++i) con[x][i]=Stack[i];
}

inline void pushdown(int x)
{
    ll tmp=fir[x];
    for (int i=st[x]; i<=ed[x]; ++i) h[i]+=tmp, tmp+=dif[x], h[i]+=add[x];
    fir[x]=dif[x]=add[x]=0;
}

inline ll ask(int x)
{
    int l=1, r=num[x];
    ll h1, h2, h3;
    while (l<=r)
    {
        int mid=(l+r)>>1;
        h1=cal(con[x][mid-1]), h2=cal(con[x][mid]), h3=cal(con[x][mid+1]);
        if (h1<h2 && h2<h3) l=mid+1;
        else if (h1>h2 && h2>h3) r=mid-1;
        else return h2;
    }
}

int main()
{
    read(n);
    for (int i=1,x; i<=n; ++i) read(x), h[i]=h[i-1]+x;
    h[0]=h[n+1]=-INF;
    int block=(int)sqrt(n), cnt=0;
    if (n%block) cnt=n/block+1;
    else cnt=n/block;
    for (int i=1; i<=n; ++i) bel[i]=(i-1)/block+1;
    for (int i=1; i<=cnt; ++i) st[i]=(i-1)*block+1, ed[i]=min(n,i*block);
    for (int i=1; i<=cnt; ++i) build(i);
    int m;read(m);
    ll k,ans=0; int l,r;
    for (int i=1,opt,x,y; i<=m; ++i)
    {
        read(opt);read(x);read(y);
        if (!opt)
        {
            read(k);
            l=bel[x], r=bel[y];
            ll tmp=k*(st[l+1]-x+1);
            for (int i=l+1; i<=r-1; ++i) fir[i]+=tmp, dif[i]+=k, tmp+=block*k;
            pushdown(l);
            tmp=k;
            for (int i=x; i<=min(y,ed[l]); ++i) h[i]+=tmp, tmp+=k;
            build(l);
            pushdown(r);
            if (l^r)
            {
                tmp=k*(st[r]-x+1);
                for (int i=st[r]; i<=y; ++i) h[i]+=tmp, tmp+=k;
            }
            tmp=k*(y-x+1);
            for (int i=y+1; i<=ed[r]; ++i) h[i]+=tmp;
            build(r);
            for (int i=r+1; i<=cnt; ++i) add[i]+=tmp;
        }
        else
        {
            l=bel[x], r=bel[y];
            ans=-INF;
            for (int i=l+1; i<r; ++i) chkMax(ans,ask(i));
            for (int i=x; i<=min(y,ed[l]); ++i) chkMax(ans,cal(i));
            if (l^r)
            {
                for (int i=st[r]; i<=y; ++i) chkMax(ans,cal(i));
            }
            write(ans,'\n');
        }
    }
    IO::flush();
    return 0;
}

转载于:https://www.cnblogs.com/G-hsm/p/11445352.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值