【几何】[HNOI2008][BZOJ1007]水平可见直线
题目描述
在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条直线两两不重合.求出所有可见的直线.
第一行为N(0 < N < 50000),接下来的N行输入Ai,Bi
从小到大输出可见直线的编号,两两中间用空格隔开,最后一个数字后面也必须有个空格
样例输入
3
-1 0
1 0
0 0
样例输出
1 2
题目分析
首先我们看到题目可以发现,我们其实维护的是一个向下的凸包,那么我们按照斜率优化的方法搞一搞,首先我们将每一条直线按照K进行排序,然后我们发现如果上一个交点在当前直线的下方,那么当前直线一定覆盖了上一条直线,这个画个图就明白了。然后最后栈里剩下的直线就是答案(这道题目主要是难写)
代码
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <stack>
#include <cmath>
#define mcp(a,b) fabs((a)-(b))<eps
using namespace std;
const int MAXN = 500000;
const double eps = 1e-8;
const int INF = 1000000000;
struct Point{
double x, y;
Point(){x=y=0;}
};
struct Line{
double a, b;
int id;
Point GetD(const Line& c){
Point ret;
ret.x = (1.0 * (c.b - b)) / (a - c.a);
ret.y = a * ret.x + b;
return ret;
}
bool operator == (const Line& c) {
return c.a + eps >= a && c.a - eps <= a;
}
}T[MAXN+10];
stack<Line> ans;
stack<Point> jd;
bool cmp(Line a, Line b){
if(mcp(a.a, b.a))
return a.b < b.b;
return a.a < b.a;
}
int pcmp(Point p, Line l){
double y = l.a * p.x + l.b;
if(y >= p.y-eps)
return -1;
return 1;
}
bool check[MAXN+10];
int main(){
Point tmp;
int n, tn=0;
scanf("%d", &n);
for(int i=1;i<=n;i++){
scanf("%lf%lf", &T[i].a, &T[i].b);
T[i].id = i;
}
sort(T+1, T+1+n, cmp);
for(int i=1;i<=n;i++){
while(mcp(T[i].a,T[i+1].a)) i++;
T[++tn] = T[i];
}
ans.push(T[1]);
for(int i=2;i<=tn;i++){
while(!jd.empty()){
Point tp = jd.top();
if(pcmp(tp, T[i]) <= 0){
jd.pop();
ans.pop();
}else break;
}
jd.push(T[i].GetD(ans.top()));
ans.push(T[i]);
}
while(!ans.empty()){
check[ans.top().id] = true;
ans.pop();
}
bool fir = false;
for(int i=1;i<=n;i++)
if(check[i]){
if(fir)
printf(" %d", i);
else{
printf("%d", i);
fir = true;
}
}
return 0;
}