简述
李超树得名于其发明者——杭州学军中学的李超大神。这个数据结构主要用于解决动态维护(上\下)凸包,是一个非常神仙的东西,主要难点在于处理线段树上的懒惰标记。
算法代码与解释
这个算法有下面几个函数:
- INTERSECT(k1, k2, b1, b2):求两直线交点的横坐标。
- UPDATE(k, b, l, r, p):向李超树中加入直线。
- QUERY(qx, l, r, p):询问当横坐标为\(qx\)时纵坐标的高度。
我们先来思考 UPDATE 操作。考虑以下几种情形:
- 如果我们发现该区间未储存直线,那么我们直接赋值即可。
- 如果我们发现原先有了直线,那么求出两端点上两直线的函数值,待加入直线在\(l\)上的函数值为\(ff\),在\(r\)上的函数值为\(fb\),待比较直线在\(l\)上的函数值为\(gf\),在\(r\)上的函数值为\(gb\)。我们考虑下面几种情况:
- 如果两直线平行(也就是\(ff,fb\)与\(gf,gb\)有相同的大小关系时),那么我们直接比较高度取最高\最低的直线。
- 如果两直线有交点,我们求出交点的横坐标\(intx\),分别进行考虑进行左右儿子更新即可。
例题
// BZ1568.cpp
#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define lson (p << 1)
#define rson ((p << 1) | 1)
using namespace std;
const int MAX_Q = 100020, MAX_N = 50010, ENDPOINT = 50000;
int n, tmpx;
char opt[10];
double lnk[MAX_N << 2], lnb[MAX_N << 2], tmpw, tmpz;
bool tag[MAX_N << 2];
double intersect(double k1, double b1, double k2, double b2) { return 1.0 * (b2 - b1) / (k1 - k2); }
void update(double k, double b, int l, int r, int p)
{
if (tag[p])
{
double ff = k * l + b, gf = lnk[p] * l + lnb[p], fb = k * r + b, gb = lnk[p] * r + lnb[p];
if (ff <= gf && fb <= gb)
return;
else if (ff >= gf && fb >= gb)
{
lnk[p] = k, lnb[p] = b;
return;
}
double intx = intersect(k, b, lnk[p], lnb[p]);
if (ff >= gf)
if (intx <= mid)
update(k, b, l, mid, lson);
else
update(lnk[p], lnb[p], mid + 1, r, rson), lnk[p] = k, lnb[p] = b;
else if (intx > mid)
update(k, b, mid + 1, r, rson);
else
update(lnk[p], lnb[p], l, mid, lson), lnk[p] = k, lnb[p] = b;
}
else
lnk[p] = k, lnb[p] = b, tag[p] = true;
}
double query(int qx, int l, int r, int p)
{
double ans = 0;
if (tag[p])
ans = max(ans, 1.0 * qx * lnk[p] + lnb[p]);
if (l == r)
return ans;
if (qx <= mid)
ans = max(ans, query(qx, l, mid, lson));
else
ans = max(ans, query(qx, mid + 1, r, rson));
return ans;
}
int main()
{
scanf("%d", &n);
while (n--)
{
scanf("%s", opt + 1);
if (opt[1] == 'Q')
scanf("%d", &tmpx), printf("%d\n", (int)(query(tmpx, 1, ENDPOINT, 1) / 100.0));
else
scanf("%lf%lf", &tmpw, &tmpz), update(tmpz, tmpw - tmpz, 1, ENDPOINT, 1);
}
return 0;
}
// BZ1568.cpp
#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define lson (p << 1)
#define rson ((p << 1) | 1)
using namespace std;
const int MAX_Q = 100020, MAX_N = 50010, ENDPOINT = 50000;
int n, tmpx;
char opt[10];
double lnk[MAX_N << 2], lnb[MAX_N << 2], tmpw, tmpz;
bool tag[MAX_N << 2];
double intersect(double k1, double b1, double k2, double b2) { return 1.0 * (b2 - b1) / (k1 - k2); }
void update(double k, double b, int l, int r, int p)
{
if (tag[p])
{
double ff = k * l + b, gf = lnk[p] * l + lnb[p], fb = k * r + b, gb = lnk[p] * r + lnb[p];
if (ff <= gf && fb <= gb)
return;
else if (ff >= gf && fb >= gb)
{
lnk[p] = k, lnb[p] = b;
return;
}
double intx = intersect(k, b, lnk[p], lnb[p]);
if (ff >= gf)
if (intx <= mid)
update(k, b, l, mid, lson);
else
update(lnk[p], lnb[p], mid + 1, r, rson), lnk[p] = k, lnb[p] = b;
else if (intx > mid)
update(k, b, mid + 1, r, rson);
else
update(lnk[p], lnb[p], l, mid, lson), lnk[p] = k, lnb[p] = b;
}
else
lnk[p] = k, lnb[p] = b, tag[p] = true;
}
double query(int qx, int l, int r, int p)
{
double ans = 0;
if (tag[p])
ans = max(ans, 1.0 * qx * lnk[p] + lnb[p]);
if (l == r)
return ans;
if (qx <= mid)
ans = max(ans, query(qx, l, mid, lson));
else
ans = max(ans, query(qx, mid + 1, r, rson));
return ans;
}
int main()
{
scanf("%d", &n);
while (n--)
{
scanf("%s", opt + 1);
if (opt[1] == 'Q')
scanf("%d", &tmpx), printf("%d\n", (int)(query(tmpx, 1, ENDPOINT, 1) / 100.0));
else
scanf("%lf%lf", &tmpw, &tmpz), update(tmpz, tmpw - tmpz, 1, ENDPOINT, 1);
}
return 0;
}
// BZ1568.cpp #include <bits/stdc++.h> #define mid ((l + r) >> 1) #define lson (p << 1) #define rson ((p << 1) | 1) using namespace std; const int MAX_Q = 100020, MAX_N = 50010, ENDPOINT = 50000; int n, tmpx; char opt[10]; double lnk[MAX_N << 2], lnb[MAX_N << 2], tmpw, tmpz; bool tag[MAX_N << 2]; double intersect(double k1, double b1, double k2, double b2) { return 1.0 * (b2 - b1) / (k1 - k2); } void update(double k, double b, int l, int r, int p) { if (tag[p]) { double ff = k * l + b, gf = lnk[p] * l + lnb[p], fb = k * r + b, gb = lnk[p] * r + lnb[p]; if (ff <= gf && fb <= gb) return; else if (ff >= gf && fb >= gb) { lnk[p] = k, lnb[p] = b; return; } double intx = intersect(k, b, lnk[p], lnb[p]); if (ff >= gf) if (intx <= mid) update(k, b, l, mid, lson); else update(lnk[p], lnb[p], mid + 1, r, rson), lnk[p] = k, lnb[p] = b; else if (intx > mid) update(k, b, mid + 1, r, rson); else update(lnk[p], lnb[p], l, mid, lson), lnk[p] = k, lnb[p] = b; } else lnk[p] = k, lnb[p] = b, tag[p] = true; } double query(int qx, int l, int r, int p) { double ans = 0; if (tag[p]) ans = max(ans, 1.0 * qx * lnk[p] + lnb[p]); if (l == r) return ans; if (qx <= mid) ans = max(ans, query(qx, l, mid, lson)); else ans = max(ans, query(qx, mid + 1, r, rson)); return ans; } int main() { scanf("%d", &n); while (n--) { scanf("%s", opt + 1); if (opt[1] == 'Q') scanf("%d", &tmpx), printf("%d\n", (int)(query(tmpx, 1, ENDPOINT, 1) / 100.0)); else scanf("%lf%lf", &tmpw, &tmpz), update(tmpz, tmpw - tmpz, 1, ENDPOINT, 1); } return 0; }