CF1137 Train Car Selection
题意
给定一个数列,初始有\(n\)个\(0\)。
有\(m\)个操作,为以下三种:
- 在最前面插入\(k\)个\(0\)。
- 在最后面插入\(k\)个\(0\)。
- 对于第\(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]));
}
}
后记
鸽子更博了,在家都变懒了,不想开学了
* 生而自由 爱而无畏 *