- 題意(原題):
- 給出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;
}