1007: [HNOI2008]水平可见直线
题目:传送门
题解:
蒟蒻在bzoj上做的第一道计算几何
其实这道题并不难...(所以我A了)
仔细想想不难发现,其实我们只需要维护一个下凸的图形...
只有在这个图形上的直线才不会被覆盖,也就是可以被上帝直线看到的孩子
为什么呢...自己画个图模拟吧。
那么具体的做法我们就要采用单调栈啦!
把给出的直线按照斜率从小到大排序(如果斜率相同的话就按照b来排)
然后一个一个放入我们强大的单调栈中,在加入的同时我们当然还要进行维护:
如果栈顶的直线与新加直线的交点在前一个的左边,那么就更新栈顶直线。
很容易想到啊:一个下凸的图形,要求每条直线不被互相覆盖,那么不仅斜率递增,交点的横坐标也是不断增大的。
码个代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 #define eps 1e-8 7 using namespace std; 8 int n; 9 struct node 10 { 11 double k,b;int id; 12 }a[51000],st[51000];int top; 13 bool v[51000]; 14 bool cmp(node n1,node n2) 15 { 16 if(n1.k==n2.k)return n1.b<n2.b; 17 else return n1.k<n2.k; 18 } 19 double dis(node n1,node n2){return (n2.b-n1.b)/(n1.k-n2.k);} 20 void add(node x) 21 { 22 while(top) 23 { 24 if(st[top].k==x.k)top--; 25 else if(top>1 && dis(x,st[top-1])<=dis(st[top],st[top-1]))top--; 26 else break; 27 } 28 st[++top]=x; 29 } 30 void solve() 31 { 32 for(int i=1;i<=n;i++)add(a[i]); 33 for(int i=1;i<=top;i++)v[st[i].id]=1; 34 for(int i=1;i<=n;i++) 35 if(v[i]) 36 printf("%d ",i); 37 printf(" "); 38 } 39 int main() 40 { 41 scanf("%d",&n);top=0; 42 memset(v,false,sizeof(v)); 43 for(int i=1;i<=n;i++) 44 { 45 double k,b; 46 scanf("%lf%lf",&k,&b); 47 a[i].k=k;a[i].b=b;a[i].id=i; 48 } 49 sort(a+1,a+n+1,cmp); 50 solve(); 51 return 0; 52 }