http://www.elijahqi.win/archives/3484
Description
在xoy直角坐标平面上有n条直线L1,L2,…Ln,若在y值为正无穷大处往下看,能见到Li的某个子线段,则称Li为
可见的,否则Li为被覆盖的.
例如,对于直线:
L1:y=x; L2:y=-x; L3:y=0
则L1和L2是可见的,L3是被覆盖的.
给出n条直线,表示成y=Ax+B的形式(|A|,|B|<=500000),且n条直线两两不重合.求出所有可见的直线.
Input
第一行为N(0 < N < 50000),接下来的N行输入Ai,Bi
Output
从小到大输出可见直线的编号,两两中间用空格隔开,最后一个数字后面也必须有个空格
Sample Input
3
-1 0
1 0
0 0
Sample Output
1 2
HINT
Source
证明:http://trinkle.blog.uoj.ac/blog/235
补充:上午看了很久这个 因为数学基础太差 这里有一个对偶的问题
我们可以更加形象的不通过画图想象的问题
首先我能看见的一定都是在一个下凸壳上的东西 那么我将其转化为点值之后即y=kx+b
点=(k,-b) 转化为这之后我们求上凸壳即可 这是为什么 我们考虑下凸壳中最后一条直线我们一定是斜率最大的点 然后我们考虑两条直线k1,-b1 k,-b
我们重新在点的这个坐标中把他们的斜率写出来 发现正好可以用k1,k,b,b1相互表示并且凹凸性一致 其他详细证明参照上面神犇blog,蒟蒻elijahqi对此只是浅显的了解
#include<cstdio>
#include<cctype>
#include<algorithm>
#define ll long long
using namespace std;
inline char gc(){
static char now[1<<16],*S,*T;
if(T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
return *S++;
}
inline int read(){
int x=0,f=1;char ch=gc();
while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
return x*f;
}
const int N=5e4+10;
int n,q[N],top;
struct node{int x,y,id;}a[N];
inline node operator-(const node &a,const node &b){return (node){a.x-b.x,a.y-b.y};}
inline ll operator*(const node &a,const node &b){return (ll)a.x*b.y-(ll)a.y*b.x;}
inline ll cross(const node &a,const node &b,const node &c){return(b-a)*(c-a);}
inline bool cmp(const node &a,const node &b){return a.x==b.x?a.y<b.y:a.x<b.x;}
int main(){
// freopen("bzoj1007.in","r",stdin);
n=read();
for (int i=1;i<=n;++i) a[i]=(node){read(),-read(),i};
sort(a+1,a+n+1,cmp);
for (int i=1;i<=n;++i){
if (a[i].x!=a[i-1].x||i==1) {
while(top>1&&cross(a[q[top-1]],a[q[top]],a[i])<=0)--top;q[++top]=i;
}
}
for (int i=1;i<=top;++i) q[i]=a[q[i]].id;sort(q+1,q+top+1);
for (int i=1;i<=top;++i) printf("%d ",q[i]);puts("");
return 0;
}