CF1137 Train Car Selection

题意

给定一个数列,初始有(n)(0)
(m)个操作,为以下三种:

  1. 在最前面插入(k)(0)
  2. 在最后面插入(k)(0)
  3. 对于第(i)个数,加上(k(i-1)+b)

每次操作完毕后,输出数列中的最小值,以及第一次出现的位置。

(n leq 10^9,m leq 3 imes 10^5)

传送门

思路

对于一段(0),其实真正有用的只有第一个,只要记录第一个的位置就好。

首先对于第一个操作,因为只有加法,显然第一个(0)肯定是最小的,我们可以把之前的所有都忽略。

然后就变成了加某个一次函数,求最小值。

考虑数列中能成为最小值的数,这个非常(对我来说 bushi)套路,把位置当做横坐标,值当做纵坐标,在下凸壳上的点才有可能成为最小值。

那么我们用栈维护下凸壳即可。

全局的加法可以维护两个全局变量实现,新加的点减掉前面的影响即可。

#include <bits/stdc++.h>
typedef long long ll;
long long n,k,b;
int Q,opt,x,y,tp;
struct point{
	ll x,y;
}st[300005];
inline point operator -(point x,point y){
	return point{x.x-y.x,x.y-y.y};
}
ll cal(point x){ return x.y+b+(x.x-1)*k; }
ll cross(point x,point y){ return x.x*y.y-x.y*y.x; } 
int main(){
	scanf("%lld%d",&n,&Q);
	st[++tp]={1,0};
	while (Q--){
		scanf("%d",&opt);
		if (opt==1){
			scanf("%d",&x);
			st[tp=1]={1,0};
			k=0,b=0,n+=x;
		}else if (opt==2){
			scanf("%d",&x);
			point t={n+1,-cal(point{n+1,0})};
			n+=x;
			if (cal(st[tp])>0){
				while (tp>=2 && cross(st[tp-1]-t,st[tp]-t)<=0) tp--;
				st[++tp]=t;
			}	
		}else{
			scanf("%d%d",&y,&x);
			k+=x,b+=y;
			while (tp>=2 && cal(st[tp])>=cal(st[tp-1])) tp--;
		}
		printf("%lld %lld
",st[tp].x,cal(st[tp]));
	}
} 

后记

鸽子更博了,在家都变懒了,不想开学了

原文地址:https://www.cnblogs.com/flyfeather6/p/12498719.html