半平面交(×)一次函数(√)
观察一下题面,很显然他是让你维护一个凸壳。
我们考虑把所有直线按照斜率从小到大排序,相同斜率的取在 (y) 轴上的截距最大的。
然后我们类似于求凸包地,依次将直线试着加入一个单调栈中,弹出一条直线的条件就是它被挡住了。
考虑栈顶被挡住的情况:由于我们是按照斜率从小到大加直线的,画一个图不难发现当前直线和栈顶的交点,在栈顶和栈顶下面一个的交点的左边(可以重合),满足条件时就把栈顶弹掉。
求交点就是解一个二元一次方程组。
最后留在栈中的就是可以被看见的直线。
方便起见,可以特判一下去重后 (n = 1) 的情况。
参考代码:
#include <algorithm>
#include <cstdio>
using namespace std;
const int _ = 5e4 + 5;
int n, now, top, ans[_]; struct node { double k, b; int id; } t[_], stk[_];
int cmp(node x, node y) { return x.k < y.k || (x.k == y.k && x.b > y.b); }
double cross(node x, node y) { return (x.b - y.b) / (y.k - x.k); }
int main() {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin), freopen("cpp.out", "w", stdout);
#endif
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%lf %lf", &t[i].k, &t[i].b), t[i].id = i;
sort(t + 1, t + n + 1, cmp);
t[++now] = t[1];
for (int i = 2; i <= n; ++i)
if (t[i].k != t[i - 1].k) t[++now] = t[i];
if (now == 1) { printf("%d ", t[1].id); return 0; }
stk[++top] = t[1], stk[++top] = t[2];
for (int i = 3; i <= now; ++i) {
while (top >= 2 && cross(t[i], stk[top]) <= cross(stk[top - 1], stk[top])) --top;
stk[++top] = t[i];
}
for (int i = 1; i <= top; ++i) ans[stk[i].id] = 1;
for (int i = 1; i <= n; ++i) if (ans[i]) printf("%d ", i);
return 0;
}