【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;
}