BZOJ4700: 适者
先排序,枚举删一个点,在前面找出最优的另一个点,容易推出斜率方程,平衡树维护凸包。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=3e5+5; typedef struct node*ptr; struct node{ ptr i,j,s,t; ll w,x,y; node(){w=rand();} }*b,e[N]; void zag(ptr&o){ptr s=o->j;o->j=s->i,s->i=o,o=s;} void zig(ptr&o){ptr s=o->i;o->i=s->j,s->j=o,o=s;} void ins(ptr j,ptr&o=b){ if(!o)o=j,o->s?o->s->t=o:0,o->t?o->t->s=o:0; else if(j->x>o->x) {ins(j,o->j);if(o->j->w>o->w)zag(o);} else if(o->x>j->x) {ins(j,o->i);if(o->i->w>o->w)zig(o);} } void del(ptr j,ptr&o=b){ if(j->x>o->x)del(j,o->j); else if(o->x>j->x)del(j,o->i); else if(!o->i)o=o->j; else if(!o->j)o=o->i; else if(o->i->w>o->j->w)zig(o),del(j,o->j); else zag(o),del(j,o->i); } ptr pre(ll x,ptr o=b){ ptr s=0; while(o)x>=o->x?s=o,o=o->j:o=o->i; return s; } ptr suc(ll x,ptr o=b){ ptr s=0; while(o)o->x>=x?s=o,o=o->i:o=o->j; return s; } ll cal(ptr o,ptr s,ptr t){ ll x1=o->x-s->x,y1=o->y-s->y; ll x2=t->x-s->x,y2=t->y-s->y; return x1*y2-x2*y1; } void upd(ptr o){ if(ptr&s=o->s=pre(o->x)) while(s->s&&cal(o,s->s,s)<=0) del(s),s=pre(o->x); if(ptr&t=o->t=suc(o->x)) while(t->t&&cal(o,t,t->t)<=0) del(t),t=suc(o->x); if(!o->s||!o->t)ins(o); else if(cal(o->s,o,o->t)>0)ins(o); } ll slo(ptr s,ptr t){return(s->y-t->y)/(s->x-t->x);} ptr sol(ll k,ptr o=b){ if(o->s&&k>slo(o->s,o))return sol(k,o->i); if(o->t&&slo(o,o->t)>k)return sol(k,o->j); return o; } struct foo{int s,t;}c[N]; bool operator<(foo s,foo t){return s.t*t.s<t.t*s.s;} int n,m,d; ll k,q,s[N],r[N]; int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i){ scanf("%d%d",&c[i].s,&d); c[i].t=(d+m-1)/m; } sort(c+1,c+n+1); for(int i=1;i<=n;++i) s[i]=s[i-1]+c[i].t; for(int i=n;i;--i){ k+=(s[i]-1)*c[i].s; r[i-1]=r[i]+c[i].s; e[i].x=c[i].t; e[i].y=(s[i]-1)*c[i].s+c[i].t*r[i]; } upd(e+1); for(int i=2;i<=n;++i){ ptr j=sol(c[i].s); q=max(q,e[i].y+j->y-j->x*c[i].s); upd(e+i); } printf("%lld\n",k-q); }