BZOJ2732[HNOI2012]射箭
题目大意
给出N条垂直于x轴的线段,求最大的K,使得存在一条经过原点,开口向下的抛物线穿过前K条线段(经过端点也算穿过),保证N条线段不重叠,且都在第一象限。
输入输出
第一行为一个整数N;
接下来N行每行3个整数x,y1,y2,描述一条线段。
数据范围
N≤100000,0<x≤1e9,0<y1<y2≤1e9。
解析
设抛物线方程为ax2+bx,则y1≤ax2+bx≤y2;
可以把每条线段看成两个关于a,b的不等式,由于是一次的,可以用半平面交验证答案。于是二分K,验证是否可行。
几个细节:
1)半平面在向量哪边要想清楚;
2)由于经过端点也算穿过,所以半平面交的边上也是可行的,极端情况所有直线交于一点,不知道有没有这种数据;
3)抛物线开口向下,对称轴在第一象限,增加2个向量限制a<0,b>0;
4)为了方便判断半平面交是否为空,可以增加2个“边框”向量,但是这两个向量的起止点坐标要足够大(反正我开1e12WA了);
5)开long double;
6)全部排序后根据id加入半平面交O(nlogn)比每次二分都排一次序O(nlog2n)优秀(废话)。
代码
一直T竟然是因为求交点的方法不够优秀,代码过于丑陋必须加读入优化才能过qwq。
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <iomanip> 5 #include <cmath> 6 #include <algorithm> 7 8 typedef long long LL; 9 typedef long double LD; 10 const LD INF = 1e15; 11 struct Point { 12 LD x, y; 13 Point(LD _x = 0, LD _y = 0):x(_x), y(_y) {}; 14 }; 15 struct Vector { 16 Point st, ed; 17 int id; 18 LD arg; 19 Vector() { memset(this, 0, sizeof(Vector)); } 20 Vector(Point _st, Point _ed):st(_st), ed(_ed), id(0) {}; 21 bool operator <(const Vector &) const; 22 friend LD cross(const Vector &, const Vector &); 23 friend Point intersection(const Vector &, const Vector&); 24 } line[200010]; 25 int N; 26 27 bool judge(int); 28 bool check(const Vector &, const Vector &, const Vector &); 29 inline char gc(); 30 inline int read(); 31 32 int main() { 33 #ifndef ONLINE_JUDGE 34 freopen("2732.in", "r", stdin); 35 freopen("2732.out", "w", stdout); 36 #endif 37 N = read(); 38 line[0] = Vector(Point(0, -INF), Point(0, INF)); 39 line[1] = Vector(Point(-INF, 0), Point(INF, 0)); 40 for (int i = 1; i <= N; i++) { 41 LD x = read(), y1 = read(), y2 = read(); 42 line[i << 1] = Vector(Point(-1, y1 / x + x), Point(1, y1 / x - x)); 43 line[i << 1 | 1] = Vector(Point(1, y2 / x - x), Point(-1, y2 / x + x)); 44 line[i << 1].id = line[i << 1 | 1].id = i; 45 } 46 int l = 1, r = N; 47 N = (N << 1 | 1); 48 line[++N] = Vector(Point(-INF, INF), Point(-INF, -INF)); 49 line[++N] = Vector(Point(INF, INF), Point(-INF, INF)); 50 for (int i = 0; i <= N; i++) 51 line[i].arg = atan2(line[i].ed.y - line[i].st.y, line[i].ed.x - line[i].st.x); 52 std::sort(line, line + N + 1); 53 while (l < r) { 54 int mid = (l + r + 1) >> 1; 55 if (judge(mid)) l = mid; 56 else r = mid - 1; 57 } 58 printf("%d\n", l); 59 60 return 0; 61 } 62 bool Vector::operator <(const Vector &v) const { 63 if (arg != v.arg) return arg < v.arg; 64 else return cross(*this, Vector(st, v.st)) <= 0; 65 } 66 LD cross(const Vector &a, const Vector &b) { 67 return (a.ed.x - a.st.x) * (b.ed.y - b.st.y) - (a.ed.y - a.st.y) * (b.ed.x - b.st.x); 68 } 69 Point intersection(const Vector &a, const Vector &b) { 70 LD s1 = cross(Vector(a.st, b.ed), a); 71 LD s2 = cross(a, Vector(a.st, b.st)); 72 LD t = s2 / (s1 + s2); 73 return Point(b.st.x + t * (b.ed.x - b.st.x), b.st.y + t * (b.ed.y - b.st.y)); 74 } 75 bool check(const Vector &a, const Vector &b, const Vector &c) { 76 Point p = intersection(a, b); 77 return cross(Vector(c.st, p), c) > 0; 78 } 79 bool judge(int lim) { 80 static Vector ls[200010], res[200010]; 81 int tot = 0, head = 0, tail = 0; 82 for (int i = 0; i <= N; i++) 83 if (line[i].id <= lim) ls[tot++] = line[i]; 84 for (int i = 0; i < tot; i++) { 85 if (i && ls[i].arg == ls[i - 1].arg) continue; 86 while (tail - head > 1 && check(res[tail - 2], res[tail - 1], ls[i])) tail--; 87 while (tail - head > 1 && check(res[head], res[head + 1], ls[i])) head++; 88 res[tail++] = ls[i]; 89 } 90 while (tail - head > 2 && check(res[tail - 2], res[tail - 1], res[head])) tail--; 91 while (tail - head > 2 && check(res[head], res[head + 1], res[tail - 1])) head++; 92 return tail - head > 2; 93 } 94 inline char gc() { 95 static char buf[1000000], *p1, *p2; 96 if (p1 == p2) p1 = (p2 = buf) + fread(buf, 1, 1000000, stdin); 97 return p1 == p2 ? EOF : *p2++; 98 } 99 inline int read() { 100 int res = 0, op = 1; 101 char ch = gc(); 102 while (ch != '-' && (ch < '0' || ch > '9')) ch = gc(); 103 if (ch == '-') op = 1, ch = gc(); 104 while (ch >= '0' && ch <= '9') 105 res = (res << 1) + (res << 3) + ch - '0', ch = gc(); 106 return res * op; 107 }