1. 程式人生 > 其它 >P3194 [HNOI2008]水平可見直線 題解

P3194 [HNOI2008]水平可見直線 題解

光看是解不出題的。

自(he) 己(wan)畫(ti)圖(jie),發現沒被擋住的多條直線會在平面上形成一個下凸包,從左到右斜率遞增。

(取自p_b_p_b題解)

於是,我們將直線以斜率為第一關鍵字,截距為第二關鍵字從大到小排序;

然後從前往後列舉直線,加入單調棧。

當單調棧內直線數量 \(\ge\) 2時,設 \(l_1\)\(l_2\) 為棧頂兩直線,\(L\) 為列舉直線,用一元二次方程算出 \(L\)\(l_1\)\(l_1\)\(l_2\) 的交點橫座標 \(x_1\)\(x_2\) 。若 \(x_2>x_1\) ,則 \(l_1\) 已經被覆蓋,退棧,重複該操作到 \(x_2<x_1\)

​ 或棧中只剩一條直線。

#include <bits/stdc++.h>
#define ll long long
#define rgi register int
using namespace std;
const int M=5e4+7;
inline int read(){
	int w=0,r=1;char c=getchar();
	while(!(isdigit(c)||c=='0'))c=getchar();
	if(c=='-')r=-1,c=getchar(); 
	while(isdigit(c))w=w*10+c-'0',c=getchar();
	return w*r;
}
int n,m,stk[M],ans[M],cnt;double a,b,r;
struct l{
	int ii;double k,b;
}p[M];
bool cmp(l aa,l bb){
	return aa.k>bb.k||(aa.k==bb.k&&aa.b>bb.b);
}
double xx(int aa,int bb){
	return (double)(p[bb].b-p[aa].b)/(p[aa].k-p[bb].k);
}
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		scanf("%lf%lf",&p[i].k,&p[i].b);
		p[i].ii=i;
	}
	sort(p+1,p+n+1,cmp);
	for(int i=1;i<=n;i++){
		if(p[i].k==p[stk[cnt]].k&&i>1)continue; 
		while(cnt>=2&&xx(stk[cnt-1],stk[cnt])<=xx(stk[cnt],i))cnt--;
		stk[++cnt]=i;
		ans[cnt]=p[i].ii;
	}
	sort(ans+1,ans+cnt+1);
	for(int i=1;i<=cnt;i++)printf("%d ",ans[i]);
	printf("\n");
	return 0;
} 
/*
3
-1 0
1 0
0 0
*/