- 题意(原题):
- 给出n条形如y=kx+b的直线,求你从所有直线的上面往下看,能看到哪些直线(只能看到一个点的不算)。
- n<=50000
- 思路:
- 将直线按k从小到大排序;k相等时按b从大到小排。
- 将前两条直线丢入栈里面,然后逐个往栈里面加直线。设栈顶为sta[top],那么如果sta[top]与sta[top-1]的交点在当前要加入的直线下面,则持续踢出栈顶直到满足条件。因为这说明sta[top]会被sta[top-1]与当前加入直线覆盖。
- 画个图你就会明白这是对的。
- 以及一些特判:丢前两条和后面的直线时都要看看斜率是否和上一条直线相等,是的话当前加入直线报废(因为按b从大到小排,前面加入的直线一定在当前加入的直线上面)。
- 这题我在luoguo和new_bzoj都A了,bzoj死活wa过不去。
- 代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
#define eps (1e-8)
using namespace std;
struct PointNode
{
double x,y;
PointNode()
{
x=y=0.0;
}
};
struct LineNode
{
int num;
double k,b;
LineNode()
{
k=b=0.0;
}
}a[51000];
int cmp(const void *xx,const void *yy)
{
LineNode d1=*(LineNode *)xx;
LineNode d2=*(LineNode *)yy;
if(abs(d1.k-d2.k)<eps)
{
if(d1.b>d2.b)return -1;
return 1;
}
if(d1.k>d2.k)return 1;
return -1;
}
PointNode jd(LineNode x,LineNode y)
{
PointNode ans;
ans.x=(y.b-x.b)/(x.k-y.k);
ans.y=ans.x*x.k+x.b;
return ans;
}
int n,top=0;
LineNode sta[51000];
bool pr[51000];
int main()
{
memset(pr,true,sizeof(pr));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
a[i].num=i;
scanf("%lf%lf",&a[i].k,&a[i].b);
}
qsort(a+1,n,sizeof(LineNode),cmp);
sta[1]=a[1];
for(int i=2;i<=n;i++)
{
if(fabs(a[1].k-a[i].k)>eps)
{
sta[2]=a[i];
break;
}
pr[a[i].num]=false;
if(i==n)
{
printf("%d \n",a[1].num);
return 0;
}
}
sta[2]=a[2];top=2;
for(int i=3;i<=n;i++)
{
if(fabs(a[i].k-sta[top].k)<eps)
{
pr[a[i].num]=false;
continue;
}
while(top>=2)
{
PointNode p=jd(sta[top],sta[top-1]);
if(a[i].k*p.x+a[i].b>p.y||fabs(a[i].k*p.x+a[i].b-p.y)<eps)
{
pr[sta[top].num]=false;
top--;
}
else break;
}
sta[++top]=a[i];
}
for(int i=1;i<=n;i++)
if(pr[i])
printf("%d ",i);
printf("\n");
return 0;
}