BZOJ2388: 旅行规划

BZOJ2388: 旅行规划

又是一道氪金题。。。

$BZOJ$氪金无极限。。。

附上大美洛谷的题面:

洛谷P4192 旅行规划

题目描述

OIVillage是一个风景秀美的乡村,为了更好的利用当地的旅游资源,吸引游客,推动经济发展,xkszltl决定修建了一条铁路将当地n个最著名的经典连接起来,让游客可以通过火车从铁路起点(1号景点)出发,依次游览每个景区。

为了更好的评价这条铁路,xkszltl为每一个景区都哦赋予了一个美观度,而一条旅行路径的价值就是它所经过的景区的美观度之和。

不过,随着天气与季节的变化,某些景点的美观度也会发生变化。

xkszltl希望为每位旅客提供最佳的旅行指导,但是由于游客的时间有限,不一定能游览全部景区,然而他们也不希望旅途过于短暂,所以每个游客都希望能在某一个区间内的车站结束旅程,而xkszltl的任务就是为他们选择一个终点使得旅行线路的价值最大。

可是当地的景点与前来观光的旅客实在是太多了,xkszltl无法及时完成任务,于是找到了准备虐杀NOI2011的你,希望你能帮助他完成这个艰巨的任务。

输入输出格式

输入格式:

 

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

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

  1. 0 x y k:表示将x到y这段铁路边上的景区的美观度加上k;

  2. 1 x y:表示有一名旅客想要在x到y这段(含x与y)中的某一站下车,你需要告诉他最大的旅行价值。

 

输出格式:

 

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

 

输入输出样例

输入样例: 
5
1 8 -8 3 -7
3
1 1 5
0 1 3 6
1 2 4
输出样例: 
9
22

说明

对于100%的数据,n,m≤100000。


题解Here!

题意就是求从$1$开始,到$[x,y]$之间某个点结束的前缀和最大的那个值,带修改。

那个修改先丢一边去。

有一个不容易想到的转化:

如果我们把下标看做横坐标,前缀和看做纵坐标,那答案肯定是在凸包的最高点上。

所以关键在于动态维护凸包。

我们有许多极好的数据结构可以动态维护凸包:$Treap$,李超树,$set$,等等。

但是这里我们选择了分块+二分维护。

为什么使用复杂度更高的算法呢?

因为我们维护的是前缀和及其修改。

如果是原序列,区间加就直接打个标记就好。

但是现在是前缀和。

于是区间加就变成了区间加首项为$k$、公差为$k$的等差数列。

但是多次修改呢?

没事,我们有高中必修:

$$\text{等差数列}\{a_i\}+\text{等差数列}\{b_i\}=\text{等差数列}\{a_i+b_i\}$$

所以直接合并等差数列就好。

对于每个块,我们维护等差数列首项$first$、公差$d$以及一个$add$标记。

于是每个位置的真实值就是:$$\text{当前值}+\text{块首项}+\text{公差}\times(\text{当前位置}-\text{块左端位置})+add$$

但是注意,我们维护的是前缀和。

所以当我们在$[l,r]$上区间加时,对于$r$之后的那些块,我们都要打上$add$标记。

而且在处理$r$所属块时,要先把区间加对在$r$之后的$r$所属块中元素的影响考虑完,才能对$r$所属块维护凸包。

复杂度$O(n\sqrt n\log_2 n)$。

记得开$long\ long$。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define MAXN 100010
#define MAXM 320
#define MAX (1LL<<61)
using namespace std;
int n,m,block;
int colour[MAXN],Left[MAXM],Right[MAXM],num[MAXM],convex[MAXM][MAXM];
int top,stack[MAXM];
long long val[MAXN],add[MAXM],first[MAXM],d[MAXM];//first item,tolerance
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
inline double slope(int x,int y){
	return 1.000*(val[x]-val[y])/(x-y);
}
void build(int x){
	top=0;
	stack[++top]=Left[x];
	for(int i=Left[x]+1;i<=Right[x];i++){
		while(top>=2&&slope(stack[top-1],stack[top])<slope(stack[top-1],i))top--;
		stack[++top]=i;
	}
	num[x]=top;
	convex[x][0]=0;convex[x][top+1]=n+1;
	for(int i=1;i<=top;i++)convex[x][i]=stack[i];
	add[x]=first[x]=d[x]=0;
}
void pushdown(int x){
	long long now=first[x];
	for(int i=Left[x];i<=Right[x];i++){
		val[i]+=add[x]+now;
		now+=d[x];
	}
	add[x]=first[x]=d[x]=0;
}
void update(int l,int r,long long k){
	long long sum;
	if(colour[l]==colour[r]){
		pushdown(colour[l]);
		sum=k;
		for(int i=l;i<=r;i++){
			val[i]+=sum;
			sum+=k;
		}
		sum=k*(r-l+1);
		for(int i=r+1;i<=Right[colour[r]];i++)val[i]+=sum;
		build(colour[l]);
		for(int i=colour[r]+1;i<=colour[n];i++)add[i]+=sum;
	}
	else{
		sum=k*(Left[colour[l]+1]-l+1);
		for(int i=colour[l]+1;i<colour[r];i++){
			first[i]+=sum;
			d[i]+=k;
			sum+=k*block;
		}
		pushdown(colour[l]);
		sum=k;
		for(int i=l;i<=Right[colour[l]];i++){
			val[i]+=sum;
			sum+=k;
		}
		build(colour[l]);
		pushdown(colour[r]);
		sum=k*(Left[colour[r]]-l+1);
		for(int i=Left[colour[r]];i<=r;i++){
			val[i]+=sum;
			sum+=k;
		}
		sum=k*(r-l+1);
		for(int i=r+1;i<=Right[colour[r]];i++)val[i]+=sum;
		build(colour[r]);
		for(int i=colour[r]+1;i<=colour[n];i++)add[i]+=sum;
	}
}
inline long long query_point(int x){
	if(x==0||x==n+1)return -MAX;
	return val[x]+first[colour[x]]+d[colour[x]]*(x-Left[colour[x]])+add[colour[x]];
}
long long query_block(int x){
	int l=1,r=num[x],mid;
	long long a1,a2,a3;
	while(l<=r){
		mid=l+r>>1;
		a1=query_point(convex[x][mid-1]);
		a2=query_point(convex[x][mid]);
		a3=query_point(convex[x][mid+1]);
		if(a1<a2&&a2<a3)l=mid+1;
		else{
			if(a1>a2&&a2>a3)r=mid-1;
			else return a2;
		}
	}
	return -MAX;
}
long long solve(int l,int r){
	long long ans=-MAX;
	if(colour[l]==colour[r])for(int i=l;i<=r;i++)ans=max(ans,query_point(i));
	else{
		for(int i=colour[l]+1;i<colour[r];i++)ans=max(ans,query_block(i));
		for(int i=l;i<=Right[colour[l]];i++)ans=max(ans,query_point(i));
		for(int i=Left[colour[r]];i<=r;i++)ans=max(ans,query_point(i));
	}
	return ans;
}
void work(){
	int f,x,y;
	long long k;
	while(m--){
		f=read();x=read();y=read();
		if(f==0){
			k=read();
			update(x,y,k);
		}
		else printf("%lld\n",solve(x,y));
	}
}
void init(){
	n=read();
	val[0]=0;
	for(int i=1;i<=n;i++)val[i]=val[i-1]+read();
	val[0]=val[n+1]=-MAX;
	block=sqrt(n);
	for(int i=1;i<=n;i++){
		colour[i]=(i-1)/block+1;
		if(!Left[colour[i]])Left[colour[i]]=i;
		Right[colour[i]]=i;
	}
	for(int i=1;i<=colour[n];i++)build(i);
	m=read();
}
int main(){
	init();
	work();
	return 0;
}

 

posted @ 2019-03-17 22:12 符拉迪沃斯托克 阅读(...) 评论(...) 编辑 收藏
Live2D