BZOJ2388: 旅行规划
又是一道氪金题。。。
BZOJ氪金无极限。。。
附上大美洛谷的题面:
题目描述
OIVillage是一个风景秀美的乡村,为了更好的利用当地的旅游资源,吸引游客,推动经济发展,xkszltl决定修建了一条铁路将当地n个最著名的经典连接起来,让游客可以通过火车从铁路起点(1号景点)出发,依次游览每个景区。
为了更好的评价这条铁路,xkszltl为每一个景区都哦赋予了一个美观度,而一条旅行路径的价值就是它所经过的景区的美观度之和。
不过,随着天气与季节的变化,某些景点的美观度也会发生变化。
xkszltl希望为每位旅客提供最佳的旅行指导,但是由于游客的时间有限,不一定能游览全部景区,然而他们也不希望旅途过于短暂,所以每个游客都希望能在某一个区间内的车站结束旅程,而xkszltl的任务就是为他们选择一个终点使得旅行线路的价值最大。
可是当地的景点与前来观光的旅客实在是太多了,xkszltl无法及时完成任务,于是找到了准备虐杀NOI2011的你,希望你能帮助他完成这个艰巨的任务。
输入输出格式
输入格式:
第一行给出一个整数n,接下来一行给出n的景区的初始美观度。
第三行给出一个整数m,接下来m行每行为一条指令:
-
0 x y k:表示将x到y这段铁路边上的景区的美观度加上k;
-
1 x y:表示有一名旅客想要在x到y这段(含x与y)中的某一站下车,你需要告诉他最大的旅行价值。
输出格式:
对于每个询问,输出一个整数表示最大的旅行价值。
输入输出样例
说明
对于100%的数据,n,m≤100000。
题解Here!
题意就是求从1开始,到[x,y]之间某个点结束的前缀和最大的那个值,带修改。
那个修改先丢一边去。
有一个不容易想到的转化:
如果我们把下标看做横坐标,前缀和看做纵坐标,那答案肯定是在凸包的最高点上。
所以关键在于动态维护凸包。
我们有许多极好的数据结构可以动态维护凸包:Treap,李超树,set,等等。
但是这里我们选择了分块+二分维护。
为什么使用复杂度更高的算法呢?
因为我们维护的是前缀和及其修改。
如果是原序列,区间加就直接打个标记就好。
但是现在是前缀和。
于是区间加就变成了区间加首项为k、公差为k的等差数列。
但是多次修改呢?
没事,我们有高中必修:
等差数列{ai}+等差数列{bi}=等差数列{ai+bi}
所以直接合并等差数列就好。
对于每个块,我们维护等差数列首项first、公差d以及一个add标记。
于是每个位置的真实值就是:当前值+块首项+公差×(当前位置−块左端位置)+add
但是注意,我们维护的是前缀和。
所以当我们在[l,r]上区间加时,对于r之后的那些块,我们都要打上add标记。
而且在处理r所属块时,要先把区间加对在r之后的r所属块中元素的影响考虑完,才能对r所属块维护凸包。
复杂度O(n√nlog2n)。
记得开long long。
附代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | #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; } |
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步