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