【NOIP2020提高B组模拟11.26】逗气 题目大意 解题思路 代码

【NOIP2020提高B组模拟11.26】逗气
题目大意
解题思路
代码

解题思路

把贡献点和询问点都扔到数轴上
考虑分成a[j]<=c[i]和a[j]>c[i]处理
对于a[j]<=c[i]显然有(b[j]-b[k])/(c[k]-c[j])>=d[i]时j更优
那么用单调队列维护贡献点的凸壳
对于查询点直接二分即可
对于a[j]>c[i]同理

代码

#include<bits/stdc++.h>
using namespace std;
long long n,m,i,j,b[400005],mid,l,r,x,y,ans[400005],tot,v;
struct node
{
	long long a,b,c;
}s[400005];
bool cmp(node x,node y)
{
	if(x.a!=y.a)return x.a<y.a;
	else return x.c<y.c;
}
double x11(long long x,long long y)
{
	return(s[x].b-s[y].b)*1.0/(s[x].a-s[y].a);
}
double x1(long long x,long long y)
{
	return (s[x].b-s[y].b)*1.0/(s[y].a-s[x].a);
}
int main()
{
	freopen("gas.in","r",stdin);
	freopen("gas.out","w",stdout);
	scanf("%lld%lld",&n,&m);
	for(i=1;i<=n;i++)
	{
		scanf("%lld%lld",&s[i].a,&s[i].b);
		s[i].c=0;
	}
	for(i=1;i<=m;i++)
	{
		scanf("%lld%lld",&s[i+n].a,&s[i+n].b);
		s[i+n].c=i;
	}
	sort(s+1,s+n+m+1,cmp);tot=0;
	for(i=1;i<=n+m;i++)//对于a[j]<=c[i]的贡献
	{
		if(s[i].c==0)
		{
			while(tot>1&&x1(b[tot],i)<x1(b[tot-1],b[tot]))tot--;
			tot++;b[tot]=i;//单调队列维护凸壳
		}else
		{
			l=1;r=tot;x=0;
			while(l+1<r)//二分找到第一个斜率大于d[i]的点
			{
				mid=(l+r)/2;
				if(x1(b[mid-1],b[mid])<(double)s[i].b)l=mid;
				else r=mid;
			}
			if(x1(b[l],b[r])>s[i].b)x=b[l];
			else x=b[r];
			ans[s[i].c]=max(ans[s[i].c],s[x].b-abs(s[x].a-s[i].a)*s[i].b);更新答案
		}
	}tot=0;
	for(i=n+m;i>=1;i--)
	{
		if(s[i].c==0)
		{
			while(tot>1&&x11(b[tot],i)<x11(b[tot-1],b[tot]))tot--;
			tot++;b[tot]=i;
		}else
		{
			l=1;r=tot;x=0;
			while(l+1<r)
			{
				mid=(l+r)/2;
				if(x11(b[mid-1],b[mid])<(double)s[i].b)l=mid;
				else r=mid;
			}
			if(x11(b[l],b[r])>s[i].b)x=b[l];
			else x=b[r];
			ans[s[i].c]=max(ans[s[i].c],s[x].b-abs(s[x].a-s[i].a)*s[i].b);
		}
	}
	for(i=1;i<=m;i++)
	{
		printf("%lld
",ans[i]);
	}
}