【分块】[LUOGU 旅行规划] 分块+二分+凸包优化

题目:

题目链接:[LUOGU 旅行规划]
题解:
(由于这个,,我竟然还去写了二维凸包的模板题作为练习,,,然而,一点用都没有,,,,)

先解释一下题面的意思:就是一个区间加的操作,再加上一个区间的的前缀和最大值。

这个题,,我是看到分块之后才过来写的,但是,看了半个小时的题目,没有想到怎么去优化整块的处理,这样,分块的最最主要的部分就死掉了,,,没法,我就去找了题解,,竟然是凸包优化,,,想不到啊
S i S_i Si表示初始时第 i i i个位子的前缀和为 i x + S i ix+Si ix+Si,是一个一次函数。于是,就可以维护一个的凸包进行维护整块,查询的时候就在凸包上二分就可以了,由于在凸包上的 i i i是递增的,多以就可以依次加入单调栈就好。

但是这个题的代码比较难实现,所以就敲了好长时间,还参考了hzwer的代码,才能完成,,,,所以,,,(谨慎食用!!

代码:

#include<bits/stdc++.h>
#define LL long long
#define D double
using namespace std;
inline int read()
{
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
const LL ocean=1LL<<50;
const int sea=1e5+7;
const int pool=400;
int n,m,num,block,top;
int st[pool],belong[sea],l[sea],r[sea],ins[pool],cnt[pool][pool];
LL sum[sea],fir[pool],gc[pool],lazy[pool],ans;
D slop(int x,int y){return (D)(sum[y]-sum[x])/(y-x);}
void TB(int x)//凸包优化
{
	top=0; st[++top]=l[x];
	for(int i=l[x]+1;i<=r[x];i++)
	{
		while(top>1&&slop(st[top-1],st[top])<slop(st[top-1],i)) top--;
		st[++top]=i;
	}
	st[0]=0;st[top+1]=n+1; ins[x]=top;
	for(int i=0;i<=top+1;i++) cnt[x][i]=st[i];
}
void build()
{
	block=sqrt(n); num=n/block;if(n%block) num++;
	for(int i=1;i<=n;i++) belong[i]=(i-1)/block+1;
	for(int i=1;i<=num;i++) l[i]=(i-1)*block+1,r[i]=min(n,i*block);
	for(int i=1;i<=num;i++) TB(i);
}
void down(int x)
{
	LL tt=fir[x];
	for(int i=l[x];i<=r[x];i++) sum[i]+=tt,sum[i]+=lazy[x],tt+=gc[x];	
	fir[x]=gc[x]=lazy[x]=0;
}
void alter(int x,int y,int z)//修改
{
	LL tt=z*(l[belong[x]+1]-x+1);
	for(int i=belong[x]+1;i<=belong[y]-1;i++) fir[i]+=tt,tt+=z*block,gc[i]+=z;//整块 
	down(belong[x]); tt=z;
	for(int i=x;i<=min(y,r[belong[x]]);i++) sum[i]+=tt,tt+=z;//前散块 
	TB(belong[x]); down(belong[y]);
	if(belong[x]!=belong[y])
	{
		tt=z*(l[belong[y]]-x+1);
		for(int i=l[belong[y]];i<=y;i++) sum[i]+=tt,tt+=z;//后散块 
	}
	tt=z*(y-x+1);
	for(int i=y+1;i<=r[belong[y]];i++) sum[i]+=tt; TB(belong[y]);
	for(int i=belong[y]+1;i<=num;i++) lazy[i]+=tt;
}
LL ask_sk(int x)//散块询问
{
	if(x==0||x==n+1) return -ocean;
	return sum[x]+fir[belong[x]]+gc[belong[x]]*(x-l[belong[x]])+lazy[belong[x]];		
}
LL ask_zk(int x)//整块询问
{
	int l=1,r=ins[x]; LL a1,a2,a3;
	while(l<=r)
	{
		int mid=(l+r)/2;
		a1=ask_sk(cnt[x][mid-1]),a2=ask_sk(cnt[x][mid]),a3=ask_sk(cnt[x][mid+1]);
		if(a1<a2&&a2<a3) l=mid+1;
		else if(a1>a2&&a2>a3) r=mid-1;
		else return a2;
	} 	
}
LL ask(int x,int y)
{
	ans=-ocean;
	for(int i=belong[x]+1;i<belong[y];i++) ans=max(ans,ask_zk(i));
	for(int i=x;i<=min(y,r[belong[x]]);i++) ans=max(ans,ask_sk(i));
	if(belong[x]!=belong[y])
	for(int i=l[belong[y]];i<=y;i++) ans=max(ans,ask_sk(i));
	return ans;
}
int main()
{
	n=read();
	for(int i=1,x;i<=n;i++) x=read(),sum[i]=sum[i-1]+x;
	sum[0]=sum[n+1]=-ocean;
	build(); m=read();
	for(int i=1;i<=m;i++)
	{
		int s=read(),x=read(),y=read();LL z;
		if(s) printf("%lld\n",ask(x,y));
		else z=read(),alter(x,y,z);
	}
	return 0;
}

Continue……

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值