【BZOJ1007】【HNOI2008】水平可见直线

Description

  
​  传送门
  
  
  
     
  

Solution

  
​  感觉有点套路,反正我想不到。
  
  首先对于斜率相同的直线,显然除了截距最大的一条,其他都是不可见的,直接删去。
  
  观察答案的上表面,它其实是一个由所有可见直线围成的下凸包。
  
  如果将剩余直线按斜率递增作为第一关键字、截距递减按第二关键字排序,并逐个加入,维护下凸包。
  
  类似普通下凸包,我们开一个栈\(s\)表示当前凸包由哪一些直线组成。
  
  新加入一条直线时,首先它在当前局面一定会作为凸包的一部分。但是它的加入导致哪一些直线被覆盖了呢?考虑栈尾直线是否被覆盖,它被覆盖的情况如下:
  

  
  即当新直线与\(S_{t-1}\)的交点的\(x\)坐标小于等于\(S_{t-1}\)\(S_{t}\)交点的\(x\)坐标时,\(S_t\)会被覆盖。所以我们不断将被覆盖的直线从队尾弹出,最后加入新直线。
  
  
  
  
  

Code

  

#include <cstdio>
#include <algorithm>
using namespace std;
const int N=50005;
int n;
struct Line{int k,b,id;}a[N];
int sta[N],top;
bool cmp(const Line &u,const Line &v){
	if(u.k!=v.k) return u.k<v.k;
	return u.b>v.b;
}
bool cmp2(const int &u,const int &v){return a[u].id<a[v].id;}
double inter(int i,int j){
	return 1.0*(a[j].b-a[i].b)/(a[i].k-a[j].k);	
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d%d",&a[i].k,&a[i].b),a[i].id=i;
	sort(a+1,a+1+n,cmp);
	int tn=n;
	n=0;
	for(int i=1,j;i<=tn;){
		j=i;	
		a[++n]=a[i];
		for(i++;i<=tn&&a[i].k==a[j].k;i++);
	}
	for(int i=1;i<=n;i++){
		while(top>1&&inter(sta[top-1],i)<=inter(sta[top-1],sta[top]))
			top--;
		sta[++top]=i;							
	}
	sort(sta+1,sta+1+top,cmp2);
	for(int i=1;i<=top;i++) printf("%d ",a[sta[i]].id);
	return 0;
}
posted @ 2018-07-02 21:14  RogerDTZ  阅读(100)  评论(0编辑  收藏  举报