VI.[HNOI2008]水平可见直线
一开始以为这是半平面交模板;后来一想,直接求出凸包来就行了。
我们仍然将所有直线按照斜率从小到大排序。不过这时,我们只需要使用单调栈维护即可。
具体而言,设栈顶次位、首位直线分别为\(y=k_1x+b_1,y=k_2x+b_2\)
则其交点位于
\[\Bigg(\dfrac{b_1-b_2}{k_2-k_1},k_2\Big(\dfrac{b_1-b_2}{k_2-k_1}\Big)+b_2\Bigg)
\]
假设我们现在插入一条新直线\(y=k_0x+b_0\),则弹出队尾,当且仅当
\[k_2\Big(\dfrac{b_1-b_2}{k_2-k_1}\Big)+b_2\leq k_0\Big(\dfrac{b_1-b_2}{k_2-k_1}\Big)+b_0
\]
因为其分别是首位、次位直线,所以必有\(k_2-k_1>0\),所以两边直接乘上\(k_2-k_1\),最终得到判别式
\[k_2(b_1-b_2)+b_2(k_2-k_1)\leq k_0(b_1-b_2)+b_0(k_2-k_1)
\]
直接应用即可。
时间复杂度\(O(n\log n)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,k[50100],b[50100],ord[50100],s[50100],tp;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d%d",&k[i],&b[i]),ord[i]=i;
sort(ord+1,ord+n+1,[](int u,int v){return k[u]==k[v]?b[u]>b[v]:k[u]<k[v];});
s[tp=1]=ord[1];
for(int i=2;i<=n;i++){
if(k[ord[i]]==k[ord[i-1]])continue;
while(tp>=2&&1ll*k[s[tp]]*(b[s[tp-1]]-b[s[tp]])+1ll*b[s[tp]]*(k[s[tp]]-k[s[tp-1]])<=1ll*k[ord[i]]*(b[s[tp-1]]-b[s[tp]])+1ll*b[ord[i]]*(k[s[tp]]-k[s[tp-1]]))tp--;
s[++tp]=ord[i];
}
sort(s+1,s+tp+1);
for(int i=1;i<=tp;i++)printf("%d ",s[i]);puts("");
return 0;
}