题面
解法
- 半平面交模板题。
- 首先可以二分答案,然后考虑如何检验答案是否合法。
- 现在对于每一个靶子都有一个形如 l ≤ a x 2 + b x ≤ r l\leq ax^2+bx\leq r l≤ax2+bx≤r的限制, x , l , r x,l,r x,l,r为常量,那么就可以看成对 a , b a,b a,b的限制,转化成半平面交。
- 其实不需要每一次二分的时候都进行一次排序,可以先排序,然后将编号在二分范围内的半平面取出即可。
- 时间复杂度: O ( n log n ) O(n\log n) O(nlogn)
【注意事项】
说实话这道题调了我好久……
实现的时候一定要注意精度问题……
- 为了满足实际生活的限制,必须满足抛物线的 a < 0 , b > 0 a<0,b>0 a<0,b>0,那么就相当于限制半平面交必须在第二象限内,加上4条关于第二象限的限制的半平面。
- 可能会出现所有半平面交于同一点的情况,那么在删除队列中直线的过程中不能将点在直线上判断为删除该直线的条件之一。
- 在实现半平面交的过程中,一定要先弹出队尾,再弹出队首,否则会出现一些问题。这个建议自己亲身试验一下……
- e p s eps eps和 i n f inf inf一定要开大,一开始开到 1 0 − 12 10^{-12} 10−12和 1 0 12 10^{12} 1012一直过不去,然后开成 1 0 − 15 10^{-15} 10−15和 1 0 15 10^{15} 1015就过了……
- 需要注意的细节应该就这么多吧……
代码
#include <bits/stdc++.h>
#define ld long double
#define ll long long
using namespace std;
template <typename T> void read(T &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
const int N = 200010; const ld eps = 1e-15l, inf = 1e15l;
struct pt {ld x, y;};
struct line {pt a, b; ld ang; int id;} l[N], q[N], tx[N];
struct Node {ll x, y1, y2;} a[N];
pt operator + (pt a, pt b) {return (pt) {a.x + b.x, a.y + b.y};}
pt operator - (pt a, pt b) {return (pt) {a.x - b.x, a.y - b.y};}
pt operator * (pt a, ld b) {return (pt) {a.x * b, a.y * b};}
ld operator * (pt a, pt b) {return a.x * b.y - a.y * b.x;}
bool operator < (line a, line b) {return a.ang != b.ang ? a.ang < b.ang : (a.b - b.a) * (b.b - b.a) > eps;}
pt inter(line a, line b) {
pt x = a.a - b.a, y = a.b - a.a, z = b.b - b.a;
ld t = (z * x) / (y * z); return a.a + y * t;
}
bool chkr(line a, line b, line c) {
pt t = inter(a, b);
return (c.b - c.a) * (t - c.a) < 0;
}
bool judge(int tn) {
int n = 0;
for (int i = 1; i <= tn; i++) {
if (fabs(l[i].ang - l[i - 1].ang) > eps) n++;
l[n] = l[i];
}
int tl = 1, tr = 2; q[1] = l[1], q[2] = l[2];
for (int i = 3; i <= n; i++) {
while (tl < tr && chkr(q[tr - 1], q[tr], l[i])) tr--;
while (tl < tr && chkr(q[tl], q[tl + 1], l[i])) tl++;
q[++tr] = l[i];
}
while (tl < tr && chkr(q[tr - 1], q[tr], q[tl])) tr--;
while (tl < tr && chkr(q[tl], q[tl + 1], q[tr])) tl++;
return tr - tl >= 2;
}
bool check(int mid, int n) {
int tn = 0;
for (int i = 1; i <= n; i++)
if (tx[i].id <= mid) l[++tn] = tx[i];
return judge(tn);
}
int main() {
int n, tn = 0; read(n);
for (int i = 1; i <= n; i++) read(a[i].x), read(a[i].y1), read(a[i].y2);
for (int i = 1; i <= n; i++) {
ld t1 = a[i].x * a[i].x, t2 = a[i].x, tl = a[i].y1, tr = a[i].y2;
pt x = (pt) {0, tl / t2}, y = (pt) {1, (tl - t1) / t2}, t = y - x;
tx[++tn] = (line) {x, y, atan2l(t.y, t.x), i};
x = (pt) {1, (tr - t1) / t2}, y = (pt) {0, tr / t2}, t = y - x;
tx[++tn] = (line) {x, y, atan2l(t.y, t.x), i};
}
pt x = (pt) {-inf, inf}, y = (pt) {-inf, eps}, t = y - x;
tx[++tn] = (line) {x, y, atan2l(t.y, t.x), 0};
x = (pt) {-inf, eps}, y = (pt) {-eps, eps}, t = y - x;
tx[++tn] = (line) {x, y, atan2l(t.y, t.x), 0};
x = (pt) {-eps, eps}, y = (pt) {-eps, inf}, y = y - x;
tx[++tn] = (line) {x, y, atan2l(t.y, t.x), 0};
x = (pt) {-eps, inf}, y = (pt) {-inf, inf}, t = y - x;
tx[++tn] = (line) {x, y, atan2l(t.y, t.x), 0};
sort(tx + 1, tx + tn + 1); int l = 2, r = n, ans = 1;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid, tn)) ans = mid, l = mid + 1;
else r = mid - 1;
}
cout << ans << '\n';
return 0;
}