zoukankan      html  css  js  c++  java
  • 【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(nlog^2n+mlog^2n))
    空间复杂度是(O(nlog 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
    ", ans);
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    Codeforces Round #670 (Div. 2)
    BUPT训练随笔(round 5)
    BUPT训练随笔(round 4)
    BUPT训练随笔(round 3)
    BUPT训练随笔(round 2)
    BUPT训练随笔(round 1)
    ctsc&apio2018八日游
    没有标题0.0
    splay:优雅的区间暴力!
    FJOI游记(日记向 不定期更新)
  • 原文地址:https://www.cnblogs.com/abclzr/p/6622008.html
Copyright © 2011-2022 走看看