BZOJ2388: 旅行规划

分块,每一块维护前缀和,观察发现,一次区间加操作对于 L 前, R 后的区间里相对的大小关系没有影响,对于 [L,R] 里,将每个位置放在平面内作为一个点 (i,si) ,那么相当于给区间里加了一个斜率。
每个块维护一个凸包(上凸下凸应该都可以,下文以上凸为例),如果这个块被完整覆盖,就打个斜率的标记,否则暴力重构凸包。
询问时在区间里二分,找到一个最大的斜率 (u,v) 使得其加上区间的斜率标记 <0 ,这时因为上凸,斜率递减,而这之前的斜率加上标记全部 >0 ,所以 u 优于其左侧所有点,这条斜率开始之后的斜率加标记全部 <0 ,所以v优于其右侧所有点,u优于v,所以u是最大值



code:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;

const int maxn = 110000;
const int maxnn = 400;
struct point
{
    ll x,y;
    point(){}
    point(ll _x,ll _y){x=_x;y=_y;}
};
struct node
{
    ll s[maxnn];
    point p[maxnn];
    int pos[maxnn];
    int len;
    ll k;
}a[maxnn];
ll s[maxn],f[maxn];
int id[maxn],st[maxn],N;
int n,m;

ll multi(point a1,point a2,point x)
{
    a1.x-=x.x;a2.x-=x.x;
    a1.y-=x.y;a2.y-=x.y;
    return a1.x*a2.y-a2.x*a1.y;
}
double get_k(point x,point y)
{
    double tx=y.x-x.x,ty=y.y-x.y;
    return ty/tx;
}
void up(ll &x,ll y){if(y>x)x=y;}

void _build(int t)
{
    a[t].len=a[t].k=f[t]=0;
    int len=0;
    for(int i=st[t];i1];i++)
    {
        point tmp=point(i-st[t]+1,s[i]);
        while(len>1&&multi(a[t].p[len],tmp,a[t].p[len-1])>0) len--;
        a[t].p[++len]=tmp;
        a[t].pos[len]=i;
    }
    a[t].len=len;
}
int find_(int t)
{
    int l=1,r=a[t].len-1;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(get_k(a[t].p[mid],a[t].p[mid+1])+(double)a[t].k>0)l=mid+1;
        else r=mid-1;
    }
    return r+1;
}

int main()
{
    scanf("%d",&n);N=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&s[i]); s[i]+=s[i-1];
        id[i]=(i-1)/N+1;
    }
    for(int i=1;i<=id[n];i++)st[i]=(i-1)*N+1;
    st[id[n]+1]=n+1;
    for(int i=1;i<=id[n];i++) _build(i);

    scanf("%d",&m);
    while(m--)
    {
        int x;scanf("%d",&x);
        if(!x)
        {
            int l,r;ll k;scanf("%d%d%lld",&l,&r,&k);
            int t1=id[l],t2=id[r];
            if(t1==t2)
            {
                for(int i=st[t1];i1];i++)
                    s[i]=s[i]+a[t1].k*i+f[t1];
                for(int i=l;i<=r;i++)
                    s[i]+=k*(i-l+1);
                for(int i=r+1;i1];i++) s[i]+=(r-l+1)*k;
                _build(t1);
                for(int i=t1+1;i<=id[n];i++) f[i]+=(r-l+1)*k;
            }
            else
            {
                for(int i=st[t1];i1];i++)
                    s[i]=s[i]+a[t1].k*i+f[t1];
                for(int i=l;i1];i++) s[i]+=k*(i-l+1);
                _build(t1);
                for(int i=t1+1;i1)*k;
                    a[i].k+=k;
                }
                for(int i=st[t2];i1];i++)
                    s[i]=s[i]+a[t2].k*i+f[t2];
                for(int i=st[t2];i<=r;i++) s[i]+=k*(i-l+1);
                for(int i=r+1;i1];i++) s[i]+=k*(r-l+1);
                _build(t2);

                for(int i=t2+1;i<=id[n];i++)f[i]+=k*(r-l+1);
            }
        }
        else
        {
            int l,r; scanf("%d%d",&l,&r);
            int t1=id[l],t2=id[r];
            if(t1==t2)
            {
                ll ret=LLONG_MIN;
                for(int i=l;i<=r;i++) 
                    up(ret,s[i]+i*a[t1].k+f[t1]);
                printf("%lld\n",ret);
            }
            else
            {
                ll ret=LLONG_MIN;
                for(int i=l;i1];i++) 
                    up(ret,s[i]+i*a[t1].k+f[t1]);
                for(int i=st[t2];i<=r;i++)
                    up(ret,s[i]+i*a[t2].k+f[t2]);
                for(int i=t1+1;iint temp=find_(i); temp=a[i].pos[temp];
                    up(ret,s[temp]+a[i].k*temp+f[i]);
                }
                printf("%lld\n",ret);
            }
        }
    }

    return 0;
}

你可能感兴趣的:(BZOJ,分块大法好,凸包)