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 \times 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\n",st[tp].x,cal(st[tp]));
	}
} 

后记

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

posted @ 2020-03-15 17:31  flyfeather  阅读(154)  评论(0编辑  收藏  举报