【YbtOJ#853】平面标记

题目

题目链接:https://www.ybtoj.com.cn/contest/119/problem/3

(n,mleq 10^5;0<x,yleq 1000;0<k<10^6;0<a<10)

思路

(x'_i=frac{1}{x_i}),那么也就是标记 (y_i>kcdot x_i^a)
看到指数不难想到取对数,这样就转化为了 (ln(y_i)>ln(k)+aln(x))
也就是给你若干条直线,问你每一个点最早在哪一条直线的上方。
考虑整体二分,假设当前直线区间为 ([l,r]),点的区间为 ([ql,qr]),取 (mid=frac{l+r}{2}),那么只需要知道 ([ql,qr]) 的点是否在 ([l,mid]) 所构成的上凸壳内。如果是,说明 ([l,mid]) 中所有的直线都高于这个点,往右扔,否则往左扔。
然后考场上脑抽发现 (ln) 最多是 (7) 然后保留 (6) 位小数写李超树被卡到 (90) 分,考后无论怎么调精度最高 (95)
其实只需要单调栈维护一下上凸壳就可以了,然后指针扫一下可以做到 (O(nlog n))
强制在线的话就线段树上维护凸壳在线二分。复杂度 (O(nlog ^2n))

代码

#include <bits/stdc++.h>
using namespace std;

const int N=100010;
const double eps=1e-8;
int n,m,top,st[N],ans[N];

struct node
{
	double x,y;
	int id;
}a[N],b[N],sav[N];

bool cmp1(node x,node y)
{
	return (fabs(x.x-y.x)<eps)?(x.y<y.y):(x.x>y.x);
}

bool cmp2(node x,node y)
{
	return x.x<y.x;
}

double get(int i,int j)
{
	if (fabs(b[j].x-b[i].x)<eps) return 1e14;
	return (b[i].y-b[j].y)/(b[j].x-b[i].x);
}

void solve(int l,int r,int ql,int qr)
{
	if (l==r)
	{
		for (int i=ql;i<=qr;i++)
			if (a[i].y>a[i].x*b[l].x+b[l].y)
				ans[a[i].id]=l;
		return;
	}
	int mid=(l+r)>>1,pl=l-1,pr=r+1;
	for (int i=l;i<=r;i++)
		if (b[i].id<=mid) sav[++pl]=b[i];
			else sav[--pr]=b[i];
	for (int i=l;i<=pl;i++) b[i]=sav[i];
	for (int i=pr;i<=r;i++) b[i]=sav[r-i+pr];
	pl=ql-1; pr=qr+1; top=0;
	for (int i=l;i<=mid;i++)
	{
		while (top>1 && get(i,st[top-1])>=get(i,st[top])) top--;
		st[++top]=i;
	}
	for (int i=ql,j=1;i<=qr;i++)
	{
		while (j<top && get(st[j],st[j+1])<a[i].x) j++;
		if (a[i].y>a[i].x*b[st[j]].x+b[st[j]].y) sav[++pl]=a[i];
			else sav[--pr]=a[i];
	}
	for (int i=ql;i<=pl;i++) a[i]=sav[i];
	for (int i=pr;i<=qr;i++) a[i]=sav[qr-i+pr];
	solve(l,mid,ql,pl); solve(mid+1,r,pr,qr);
}

int main()
{
	freopen("analysis.in","r",stdin);
	freopen("analysis.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%lf%lf",&a[i].x,&a[i].y);
		a[i].x=log(1.0/a[i].x); a[i].y=log(a[i].y);
		a[i].id=i;
	}
	for (int i=1;i<=m;i++)
	{
		scanf("%lf%lf",&b[i].y,&b[i].x);
		b[i].y=log(b[i].y); b[i].id=i;
	}
	memset(ans,-1,sizeof(ans));
	sort(b+1,b+1+m,cmp1);
	sort(a+1,a+1+n,cmp2); 
	solve(1,m,1,n);
	for (int i=1;i<=n;i++)
		printf("%d
",ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/14421574.html