题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1007
题意:有一些一次函数,求他们上凸壳包含哪些一次函数,升序输出序号
题解:
本来是要做十连测的一道题的结果发现不会求凸壳于是(在群内dalao的怂恿下)开了这道题。
一开始我并不知道凸壳是啥,推荐一篇好文章:https://www.luogu.org/blog/LittleRewriter/solution-p3194,于是研究这个花掉半个上午(写了3K笔记)终于将这个玩意弄明白了。
凸壳求法详细见上面链接,已经讲的很清楚了,这里就不展开讲了,我只是将大体的思路总结了一下:
用一个单调栈维护栈顶的元素和栈顶元素的下面一个元素所代表的直线的交点的横坐标。然后每次用单调栈维护即可,因为注意到如果有一条新的直线与栈顶元素直线的交点在栈下面若干条直线两两交点的左边,那么栈下面的这些直线就一定是可以被这条新的直线覆盖的,就
pop()
p
o
p
(
)
一下即可
时间复杂度
O(n)
O
(
n
)
代码:
// by Balloons
// 为什么别人比你进步的快? 因为别人比你努力的多!
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() puts("okkkkkkkk")
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
/*
维护凸壳的方法确实比较巧妙,用了一个单调栈就解决了问题
*/
const int maxn=50005;
int n;
struct line{
int a,b,id;
}lin[maxn];
int cmp(line a,line b){
if(a.a==b.a)return a.b>b.b;
return a.a<b.a;
}
int mystk[maxn],top=0,ans[maxn];
// 特别注意返回值是 double!!因为有可能同时满足两个函数的值不是整数!!
inline double getx(int ix,int iy){
return (1.0*(lin[iy].b-lin[ix].b))/(lin[ix].a-lin[iy].a);
}
inline int read(){
int x=0,f=1;
char ch;
for(ch=getchar();ch<'0'||ch>'9';ch=getchar())
if(ch=='-')f=-f;
for(;ch>='0'&&ch<='9';ch=getchar())
x=x*10+ch-'0';
return x*f;
}
int main(){
n=read();
for(int i=1;i<=n;i++)lin[i].a=read(),lin[i].b=read(),lin[i].id=i;
sort(lin+1,lin+n+1,cmp);
for(int i=1;i<=n;i++){
if(lin[i].a==lin[i-1].a&&i!=1)continue; // 忽略掉斜率相同截距不是最大的
while(top>1&&getx(mystk[top],i)<=getx(mystk[top],mystk[top-1]))--top;// 如果新的交点在原先交点的左边,说明栈顶元素一定被覆盖
mystk[++top]=i;
ans[top]=lin[i].id;
}
sort(ans+1,ans+top+1);
for(int i=1;i<=top;i++)printf("%d ",ans[i]);
puts("");
return 0;
}