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]));
    }
} 

后记

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

猜你喜欢

转载自www.cnblogs.com/flyfeather6/p/12498719.html
car
今日推荐