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);
}
posted @ 2016-12-01 23:44  f321dd  阅读(195)  评论(0编辑  收藏  举报