【BZOJ 2646】【NEERC 2011】flight

http://www.lydsy.com/JudgeOnline/problem.php?id=2646
夏令营alpq654321讲课时说这道题很简单但并没有几个人提交,最近想复习一下线段树,脑袋一热就开始写这道题。。。
询问\([i,j]\)内的抛物线在\([l,r]\)上的最大值,最大值只会出现在抛物线的极值处和\(l\)\(r\)处。
对于出现在极值处的最大值我用线段树套平衡树解决(看claris大佬用KDTree做的orzorzorz)
对于出现在\(l\)\(r\)处的最大值,对抛物线的编号建立线段树,线段树每个节点表示这个编号区间内的抛物线的上轮廓线,然后二分一下就可以了。
初始化时不断地归并两个孩子的上轮廓线作为自己的上轮廓线。
求上轮廓线时先找出两个上轮廓线所有的断点,再在两个断点之间讨论两个上轮廓线的交点。
一开始我讨论交点直接用一元二次方程求根公式,但这样会出现非常大的精度问题!(有几个情况精度很严重,比如一个抛物线与另一个抛物线相切)
看了claris大佬的代码,先求第一个交点,讨论一下,再求第二个交点再讨论一下,这样可以避免上述情况orzorzorz
这道题我写挂了好几次,也重写了好几次,出现了好几个错误:

  1. 建树套树时依次插入很费时,最好直接递归建树qwq我好蠢啊
  2. 归并两个上轮廓线时出现的精度问题qwq
  3. 求二次函数时出现的精度问题qwq
  4. lower_bound慢?手写二分。。。动态开点慢?手写内存池。。。
  5. 加了inline本地AC提交RE???还是不加inline吧。。。

时间复杂度\(O(n\log^2n+mlog^2n)\)
空间复杂度是\(O(n\log n)\)的!
总的来说本题需要卡精度+卡常(只要树套树不依次插入貌似就不会T了?)

#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;

inline double max(const double &a, const double &b) {return a > b ? a : b;}

const int N = 50003;
const double eps = 1e-8;

int n, m;

struct nodeL {
	double a, b, c; int p, x, y, pr;
	inline double get_key(double X) {
		return a * X * X + b * X + c;
	}
} L[N];

namespace Splay {
	struct node *null;
	struct node {
		node *ch[2], *fa;
		int pos, num, ma;
		int pl() {return fa->ch[1] == this;}
		void setc(node *r, int c) {ch[c] = r; if (r != null) r->fa = this;}
		void count() {ma = max(num, max(ch[0]->ma, ch[1]->ma));}
	} *root[N << 2], *tt, pool[N * 30];
	int rtn, top = 0;
	
	node *newnode(node *f, int nu1, int nu2, int nu3) {
		node *t = pool + top; ++top;
		t->ch[0] = t->ch[1] = null;
		t->fa = f;
		t->pos = nu1; t->num = nu2; t->ma = nu3;
		return t;
	}
	
	void rotate(node *r) {
		node *f = r->fa;
		int c = r->pl();
		if (f == root[rtn]) r->fa = null, root[rtn] = r;
		else f->fa->setc(r, f->pl());
		f->setc(r->ch[c ^ 1], c);
		r->setc(f, c ^ 1);
		f->count();
	}
	void splay(node *r, node *tar = null) {
		for (; r->fa != tar; rotate(r))
			if (r->fa->fa != tar) rotate(r->pl() == r->fa->pl() ? r->fa : r);
		r->count();
	}
	
	int fornow;
	
	int le(node *r, double tmp) {
		if (r == null) return -0x7fffffff;
		if (r->pos >= tmp) return le(r->ch[0], tmp);
		else if (r->pos > (fornow = le(r->ch[1], tmp))) {tt = r; return r->pos;}
		else return fornow;
	}
	
	int ri(node *r, double tmp) {
		if (r == null) return 0x7fffffff;
		if (r->pos <= tmp) return ri(r->ch[1], tmp);
		else if (r->pos < (fornow = ri(r->ch[0], tmp))) {tt = r; return r->pos;}
		else return fornow;
	}
		
	int get_max(double l, double r) {
		node *tl, *tr;
		le(root[rtn], l); tl = tt;
		ri(root[rtn], r); tr = tt;
		splay(tl); splay(tr, root[rtn]);
		return root[rtn]->ch[1]->ch[0]->ma;
	}
		
	void initnull() {null = newnode(null, 0, 0, 0); null->fa = null->ch[0] = null->ch[1] = null;}
	
	int id[N], cnt;
	bool cmp(int x, int y) {return L[x].x == L[y].x ? L[x].y < L[y].y : L[x].x < L[y].x;}
	
	node *BuildTree(int l, int r, node *f) {
		if (l > r) return null;
		int mid = (l + r) >> 1;
		node *t = newnode(f, L[id[mid]].x, L[id[mid]].y, L[id[mid]].y);
		if (mid == 0) t->pos = -1, t->num = t->ma = 0;
		if (mid == cnt + 1) t->pos = N, t->num = t->ma = 0;
		t->ch[0] = BuildTree(l, mid - 1, t);
		t->ch[1] = BuildTree(mid + 1, r, t);
		t->count();
		return t;
	}
	
	void init(int rt_num, int l, int r) {
		cnt = 0;
		for (int i = l; i <= r; ++i) id[++cnt] = i;
		stable_sort(id + 1, id + cnt + 1, cmp);
		root[rt_num] = BuildTree(0, cnt + 1, null);
		if (l == r) return;
		int mid = (l + r) >> 1;
		init(rt_num << 1, l, mid);
		init(rt_num << 1 | 1, mid + 1, r);
	}
	
	int query(int rt_num, int l, int r, int L, int R, double keyl, double keyr) {
		if (L <= l && r <= R) {
			rtn = rt_num;
			return get_max(keyl, keyr);
		}
		int mid = (l + r) >> 1, ans = 0;
		if (L <= mid) ans = max(ans, query(rt_num << 1, l, mid, L, R, keyl, keyr));
		if (R > mid) ans = max(ans, query(rt_num << 1 | 1, mid + 1, r, L, R, keyl, keyr));
		return ans;
	}
}

namespace SegmentTree {
	struct node {
		int id; double l, r;
		node (int _id = 0, double _l = 0, double _r = 0) : id(_id), l(_l), r(_r) {}
		double get(double X) {return L[id].a * X * X + L[id].b * X + L[id].c;}
		bool operator < (const node &A) const {
			return r < A.r;
		}
	};
	vector <node> T[N << 2], c;
	vector <double> q;
	
	double pos, tl, tr;
	int id[N], cnt = 0, tot;
	
	double cal(int x, int y, double l, double r) {
		double a = L[x].a - L[y].a, b = L[x].b - L[y].b, c = L[x].c - L[y].c;
		double delta = b * b - 4 * a * c, po;
		if (1.0 * (L[x].x - L[x].p) * L[y].y == 1.0 * L[x].y * (L[y].x - L[y].p)) {
			if (fabs(b) < eps) return r;
			else if ((po = -c / b) > l + eps && po < r) return po;
			else return r;
		}
		if (delta < -eps) return r;
		delta = sqrt(delta);
		tl = (-b + delta) / 2 / a;
		tr = (-b - delta) / 2 / a;
		if (tl < l + eps || tl > r - eps) tl = -1;
		if (tr < l + eps || tr > r - eps) tr = -1;
		po = r;
		if (tl != -1) po = min(po, tl);
		if (tr != -1) po = min(po, tr);
		return po;
	}
	
	void merge(vector <node> &A, vector <node> &B, vector <node> &C) {
		q.clear(); c.clear();
		double l, r, mid;
		int lena = A.size(), lenb = B.size(), tmpa = 0, tmpb = 0, tot = 0;
		q.push_back(-N);
		while (tmpa < lena && tmpb < lenb) {
			if (A[tmpa].r < B[tmpb].r) {
				if (A[tmpa].r > q[tot] + eps) q.push_back(A[tmpa].r), ++tot;
				++tmpa;
			} else {
				if (B[tmpb].r > q[tot] + eps) q.push_back(B[tmpb].r), ++tot;
				++tmpb;
			}
		}
		
		while (tmpa < lena) {if (A[tmpa].r > q[tot] + eps) q.push_back(A[tmpa].r), ++tot; ++tmpa;}
		while (tmpb < lenb) {if (B[tmpb].r > q[tot] + eps) q.push_back(B[tmpb].r), ++tot; ++tmpb;}
		tmpa = tmpb = 0;
		for (int i = 0; i < tot; ++i) {
			l = q[i]; r = q[i + 1];
			while (tmpa < lena && A[tmpa].r + eps < r) ++tmpa;
			while (tmpb < lenb && B[tmpb].r + eps < r) ++tmpb;
			if (tmpa == lena || A[tmpa].l > l + eps) {c.push_back(node(B[tmpb].id, l, r)); continue;}
			if (tmpb == lenb || B[tmpb].l > l + eps) {c.push_back(node(A[tmpa].id, l, r)); continue;}
			if (A[tmpa].id == 0 || B[tmpb].id == 0) {c.push_back(node(A[tmpa].id + B[tmpb].id, l, r)); continue;}
			
			while (r - l > eps) {
				pos = cal(A[tmpa].id, B[tmpb].id, l, r);
				mid = (l + pos) / 2;
				if (A[tmpa].get(mid) > B[tmpb].get(mid))
					c.push_back(node(A[tmpa].id, l, pos));
				else
					c.push_back(node(B[tmpb].id, l, pos));
				l = pos;
			}
		}
		
		int tt = c.size();
		for (int i, j, k = 0; k < tt; k = j) {
			for (i = k, j = k + 1; j < tt && c[i].id == c[j].id; ++j);
			C.push_back(node(c[i].id, c[i].l, c[j - 1].r));
		}
	}
	
	void BuildTree(int rt, int l, int r) {
		if (l == r) {
			T[rt].push_back(node(0, -N, L[l].p));
			T[rt].push_back(node(l, L[l].p, L[l].pr));
			T[rt].push_back(node(0, L[l].pr, N));
			return;
		}
		int mid = (l + r) >> 1;
		BuildTree(rt << 1, l, mid);
		BuildTree(rt << 1 | 1, mid + 1, r);
		
		merge(T[rt << 1], T[rt << 1 | 1], T[rt]);
	}
	
	int left, right, mid;
	double query(int rt, int l, int r, int L, int R, double q1, double q2) {
		if (L <= l && r <= R) {
			double ans = 0;
			left = 0; right = T[rt].size();
			while (left < right) {
				mid = (left + right) >> 1;
				if (T[rt][mid].r + eps < q1) left = mid + 1;
				else right = mid;
			}
			if (left != T[rt].size() && T[rt][left].l <= q1)
				ans = max(ans, T[rt][left].get(q1));
			left = 0; right = T[rt].size();
			while (left < right) {
				mid = (left + right) >> 1;
				if (T[rt][mid].r + eps < q2) left = mid + 1;
				else right = mid;
			}
			if (left != T[rt].size() && T[rt][left].l <= q2)
				ans = max(ans, T[rt][left].get(q2));
			return ans;
		}
		
		double ans = 0;
		int mid = (l + r) >> 1;
		if (L <= mid) ans = max(ans, query(rt << 1, l, mid, L, R, q1, q2));
		if (R > mid) ans = max(ans, query(rt << 1 | 1, mid + 1, r, L, R, q1, q2));
		return ans;
	}
}

int main() {
	scanf("%d", &n);
	double p, x, y;
	Splay::initnull();
	for (int i = 1; i <= n; ++i) {
		scanf("%lf%lf%lf", &p, &x, &y);
		L[i].p = p; L[i].x = x; L[i].y = y; L[i].pr = 2 * x - p;
		L[i].a = -y / ((x - p) * (x - p));
		L[i].b = -2 * x * L[i].a;
		L[i].c = y + L[i].a * x * x;
	}
	Splay::init(1, 1, n);
	SegmentTree::BuildTree(1, 1, n);
	
	scanf("%d", &m);
	int idl, idr;
	double ans, l, r;
	for (int i = 1; i <= m; ++i) {
		scanf("%d%d%lf%lf", &idl, &idr, &l, &r);
		ans = Splay::query(1, 1, n, idl, idr, l, r);
		ans = max(ans, SegmentTree::query(1, 1, n, idl, idr, l, r));
		printf("%.5lf\n", ans);
	}
	
	return 0;
}
posted @ 2017-03-26 10:44  abclzr  阅读(661)  评论(2编辑  收藏  举报