• 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$的等差数列。

    但是多次修改呢?

    没事,我们有高中必修:

    $$ ext{等差数列}{a_i}+ ext{等差数列}{b_i}= ext{等差数列}{a_i+b_i}$$

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

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

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

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

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

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

    复杂度$O(nsqrt nlog_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
    ",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;
    }
    
  • 相关阅读:
    java笔记前端部分
    java笔记数据库部分
    java笔记javaweb部分
    java笔记java部分
    JSON常用方法
    MYSQL索引
    Mysql查看执行计划参数解析(转载)
    MySQL中使用SHOW PROFILE命令分析性能的用法
    MySql查询
    DBeaver下载
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/10549297.html
Copyright © 2020-2023  润新知