[凸包][斜率]SCOI2019:湖之精灵的游戏

传送门

把叉积拆开,区间和转化为前缀和
然后前缀和就是 f [ i ] = ∑ x i ∗ y − ∑ y i ∗ x f[i]=\sum{xi}*y-\sum{yi}*x f[i]=xiyyix
要求一个j使得 f [ i ] − f [ j ] f[i]-f[j] f[i]f[j]最大(最小一样的)
那就是 ( ∑ x i ∗ y − ∑ y i ∗ x ) − ( ∑ x j ∗ y − ∑ y j ∗ x ) (\sum{xi}*y-\sum{yi}*x)-(\sum{xj}*y-\sum{yj}*x) (xiyyix)(xjyyjx)
移项得 ( ∑ x i − ∑ x j ) ∗ y − ( ∑ y i − ∑ y j ) ∗ x (\sum{xi}-\sum{xj})*y-(\sum{yi}-\sum{yj})*x (xixj)y(yiyj)x
y x ≥ ∑ y i − ∑ y j ∑ x i − ∑ x j \frac{y}{x}\ge\frac{\sum{yi}-\sum{yj}}{\sum{xi}-\sum{xj}} xyxixjyiyj
那就按斜率在凸壳上二分就完了

Code:

#include<bits/stdc++.h>
#define db double
#define int long long 
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=1e6+5;
struct point{
	int x,y;
	point(){}
	point(int _x,int _y):x(_x),y(_y){}
	friend inline point operator + (const point &a,const point &b){return point(a.x+b.x,a.y+b.y);}
	friend inline point operator - (const point &a,const point &b){return point(a.x-b.x,a.y-b.y);}
	friend inline int operator * (const point &a,const point &b){return a.x*b.y-a.y*b.x;}
}a[N],b[N],p[N];
int tota=0,totb=0;
int n;
inline void graham(){
	a[tota=1]=point(0,0);a[++tota]=p[1];
	b[totb=1]=point(0,0);b[++totb]=p[1];
	for(int i=2;i<=n;i++){
		while(tota>=2 && (a[tota]-a[tota-1])*(p[i]-a[tota-1])>=0) --tota;
		a[++tota]=p[i];
		while(totb>=2 && (p[i]-b[totb-1])*(b[totb]-b[totb-1])>=0) --totb;
		b[++totb]=p[i];
	}
}
inline db calc(point a,db k){return a.y-a.x*k;}
inline db check1(db x){
	int l=1,r=tota;
	int ans=1;
	while(l<=r){
		int mid=(l+r)>>1;
		if(calc(a[mid],x)>=calc(a[mid+1],x)) r=mid-1,ans=mid;
		else l=mid+1;
	}
	return calc(a[ans],x);
}
inline db check2(db x){
	int l=1,r=totb;
	int ans=1;
	while(l<=r){
		int mid=(l+r)>>1;
		if(calc(b[mid+1],x)>=calc(b[mid],x)) r=mid-1,ans=mid;
		else l=mid+1;
	}
	return calc(b[ans],x);
}
signed main(){
	n=read();
	p[0]=point(0,0);
	for(int x,y,i=1;i<=n;i++){
		x=read(),y=read();
		p[i]=p[i-1]+point(x,y);
	}
	graham();
	int q=read();
	while(q--){
		int x=read(),y=read();
		db k=(db)y/x;
		db f1=check1(k),f2=check2(k);
		cout<<(int)round(x*(f1-f2))<<"\n";
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值