【YbtOJ#853】平面标记
题目
题目链接:https://www.ybtoj.com.cn/contest/119/problem/3
\(n,m\leq 10^5;0<x,y\leq 1000;0<k<10^6;0<a<10\)。
思路
令 \(x'_i=\frac{1}{x_i}\),那么也就是标记 \(y_i>k\cdot x_i^a\)。
看到指数不难想到取对数,这样就转化为了 \(\ln(y_i)>\ln(k)+a\ln(x)\)。
也就是给你若干条直线,问你每一个点最早在哪一条直线的上方。
考虑整体二分,假设当前直线区间为 \([l,r]\),点的区间为 \([ql,qr]\),取 \(mid=\frac{l+r}{2}\),那么只需要知道 \([ql,qr]\) 的点是否在 \([l,mid]\) 所构成的上凸壳内。如果是,说明 \([l,mid]\) 中所有的直线都高于这个点,往右扔,否则往左扔。
然后考场上脑抽发现 \(ln\) 最多是 \(7\) 然后保留 \(6\) 位小数写李超树被卡到 \(90\) 分,考后无论怎么调精度最高 \(95\)。
其实只需要单调栈维护一下上凸壳就可以了,然后指针扫一下可以做到 \(O(n\log n)\)。
强制在线的话就线段树上维护凸壳在线二分。复杂度 \(O(n\log ^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\n",ans[i]);
return 0;
}