• 【BZOJ2388】—旅行规划(分块+凸包)


    传送门

    大意:给定一个序列,要求支持2种操作

    1、区间加

    2、询问区间ll~rr内最大的一个前缀和


    很显然如果我们把每个点的前缀和sumisum_i(i,sumi)(i,sum_i)列在二维平面上

    询问就变成了求ll~rr内的上凸包

    由于线段树之类的无法维护

    于是考虑分块维护上凸包

    每次整块修改不会改变内部凸包

    散块修改后暴力重构

    由于我们记录的是一个前缀和

    则区间加就变成了加一个等差数列

    rr之后则是加一个值

    所以维护一下firfir数列开头,deldel公差,addadd

    维护一下

    复杂度O(nnlogn)O(nsqrt n log_n)

    常数较小跑的比较快

    #include<bits/stdc++.h>
    using namespace std;
    inline int read(){
    	char ch=getchar();
    	int res=0,f=1;
    	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    	while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    	return res*f;
    }
    #define ll long long
    const int N=100005;
    const int M=350;
    const ll inf=100000000000000;
    int n,m,len,cnt,plc[N],l[M],r[M],num[M],stk[N],con[M][M];
    ll sum[N],fir[M],del[M],add[M];
    inline double slop(int x,int y){
    	return (double)(sum[y]-sum[x])/(y-x);
    }
    inline ll calc(int x){
    	if(x==0||x==n+1)return -inf;
    	return sum[x]+fir[plc[x]]+del[plc[x]]*(x-l[plc[x]])+add[plc[x]];
    }
    inline void reset(int x){
    	int top=0;stk[++top]=l[x];
    	for(int i=l[x]+1;i<=r[x];i++){
    		while(top>=2&&slop(stk[top-1],stk[top])<slop(stk[top-1],i))
    			top--;
    		stk[++top]=i;
    	}
    	num[x]=top;
    	stk[++top]=n+1;
    	for(int i=0;i<=top;i++)con[x][i]=stk[i];
    }
    inline void pushdown(int x){
    	ll tmp=fir[x];
    	for(int i=l[x];i<=r[x];i++){
    		sum[i]+=tmp,tmp+=del[x],sum[i]+=add[x];
    	}
    	del[x]=add[x]=fir[x]=0;
    }
    inline void update(int x,int y,ll k){
    	pushdown(plc[x]);
    	ll tmp=k;
    	for(int i=x;i<=min(y,r[plc[x]]);i++){
    		sum[i]+=tmp,tmp+=k;
    	}
    	reset(plc[x]);
    	for(int i=plc[x]+1;i<=plc[y]-1;i++){
    		fir[i]+=tmp,del[i]+=k,tmp+=len*k;
    	}
    	pushdown(plc[y]);
    	if(plc[x]!=plc[y]){
    		for(int i=l[plc[y]];i<=y;i++){
    			sum[i]+=tmp,tmp+=k;
    		}
    	}
    	tmp-=k;//因为为了后面的计算所以每次加一个k,但在最后一个点的时候后面是不加的,所以要减去一个k
    	for(int i=y+1;i<=r[plc[y]];i++)sum[i]+=tmp;
    	reset(plc[y]);
    	for(int i=plc[y]+1;i<=cnt;i++)add[i]+=tmp;
    }
    inline ll find(int x){
    	int l=1,r=num[x];
    	ll a1,a2,a3;
    	while(l<=r){
    		int mid=(l+r)>>1;
    		a1=calc(con[x][mid-1]),a2=calc(con[x][mid]),a3=calc(con[x][mid+1]);
    		if(a1<a2&&a2<a3)l=mid+1;
    		else if(a1>a2&&a2>a3)r=mid-1;
    		else return a2;
    	}
    }
    inline ll query(int x,int y){
    	ll ans=-inf;
    	for(int i=plc[x]+1;i<=plc[y]-1;i++){
    		ans=max(ans,find(i));
    	}
    	for(int i=x;i<=min(y,r[plc[x]]);i++){
    		ans=max(ans,calc(i));
    	}
    	if(plc[x]!=plc[y]){
    		for(int i=l[plc[y]];i<=y;i++)
    		ans=max(ans,calc(i));
    	}
    	return ans;
    }
    int main(){
    	n=read();
    	for(int i=1;i<=n;i++){
    		sum[i]=sum[i-1]+read();
    	}
    	sum[0]=sum[n+1]=-inf;
    	len=sqrt(n),cnt=(n-1)/len+1;
    	for(int i=1;i<=n;i++)plc[i]=(i-1)/len+1;
    	for(int i=1;i<=cnt;i++)l[i]=(i-1)*len+1,r[i]=i*len;
    	for(int i=1;i<=cnt;i++)reset(i);
    	m=read();
    	for(int i=1;i<=m;i++){
    		int op=read();
    		if(op==0){
    			int x=read(),y=read(),k=read();
    			update(x,y,k);
    		}
    		else {
    			int x=read(),y=read();
    			cout<<query(x,y)<<'
    ';
    		}
    	}
    }
    
  • 相关阅读:
    redis 中 set 和 hset 有什么不同,什么时候使用 hset 什么时候使用set?
    redis的底层数据结构
    python开发-实现redis中的发布订阅功能
    使用redis-py的两个类Redis和StrictRedis时遇到的坑
    python使用redis实现协同控制的分布式锁
    深入理解 Python 异步编程(上)
    linux中read,write和recv,send的区别
    socket常见问题
    python socket 编程之三:长连接、短连接以及心跳
    Flask快速入门
  • 原文地址:https://www.cnblogs.com/stargazer-cyk/p/10366343.html
Copyright © 2020-2023  润新知