[HNOI2008]水平可见直线
显然可以想到,最后留下的那些直线肯定是斜率单调递增,即形成一个下凸包的,于是可以利用类似的方式进行维护。其实个人感觉用栈维护凸包的本质是考虑每个元素加入到答案集合中后会导致哪些元素被迫出去,而由于种种原因,出去的那些元素都只会是最后的几个,还是连续的,所以可以考虑用栈维护。
以上是通法。至于题目本身,可以想到对于两条直线l1与l2,若它们斜率相同,截距大的会被看到;否则假设前者斜率小于后者,那么它们一定会存在一个交点,交点左边的可视区域为前者,其它为后者的领地。于是乎,加入一个点之后它会覆盖前面的某个节点,当且仅当它和栈顶的交点在栈顶和栈次交点的左边。写出来就可以了。
#include<cstdio>
#include<algorithm>
#define zczc
using namespace std;
const int N=50010;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w>='0'&&w<='9'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
int m,top;
struct node{
int a,b,id;
}a[N],st[N];
inline bool cmp(node s1,node s2){
return s1.a==s2.a?s1.b>s2.b:s1.a<s2.a;
}
inline bool cmp2(node s1,node s2){
return s1.id<s2.id;
}
inline double s(node s1,node s2){
return (double)(s2.b-s1.b)/(s1.a-s2.a);
}
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
read(m);
for(int i=1;i<=m;i++){read(a[i].a);read(a[i].b);a[i].id=i;}
sort(a+1,a+m+1,cmp);
for(int i=1;i<=m;i++){
if(i>1&&a[i].a==st[top].a)continue;
while(top>1&&s(st[top],st[top-1])>=s(st[top],a[i]))top--;
st[++top]=a[i];
}
sort(st+1,st+top+1,cmp2);
for(int i=1;i<=top;i++)printf("%d ",st[i].id);
return 0;
}
一如既往,万事胜意