题目大意就是 q 个询问,每次插入一条直线,或询问 横坐标 x x x 处最高点的 y y y 值是多少。
李超线段树模板题,李超线段树用来维护二维平面的直线(线段),线段树上每一个节点维护覆盖这个区间的最优势线段(即在 m i d mid mid 处取值最大的线段),类似于线段树分治,每条线段在线段树上拆分成 l o g log log 段,更新不会用 pushdown 下推标记(原来这个东西叫标记永久化),单点询问返回路径上的线段在 x x x 处的最大值。
细节学习参考:李超树学习博客
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 10;
const int N = 5e4;
int q;
char op[20];
struct Line {
//线段结构体
double k,b;
int l,r; //这条线段的区间
Line() {
}
Line(int x,int y,double ki,double bi) {
l = x, r = y;
k = ki, b = bi;
}
double calc(int x) {
//计算在 x 点的 y值
return k * x + b;
}
};
struct seg_tree {
#define lson rt << 1,l,mid
#define rson rt << 1 | 1,mid + 1,r
int tag[maxn << 2];
Line line[maxn << 2];
void build(int rt,int l,int r) {
tag[rt] = 0; line[rt].k = line[rt].b = 0;
if (l == r) return ;
int mid = l + r >> 1;
build(lson); build(rson);
}
void update(int rt,int l,int r,Line t) {
if (t.l <= l && r <= t.r) {
if (!tag[rt]) {
//还没有优势线段
tag[rt] = 1;
line[rt] = t;
} else if (line[rt].calc(l) < t.calc(l) && line[rt].calc(r) < t.calc(r)) {
//完全覆盖当前的优势线段
line[rt] = t;
} else if (line[rt].calc(l) < t.calc(l) || line[rt].calc(r) < t.calc(r)) {
//有一端覆盖
int mid = l + r >> 1;
if (line[rt].calc(mid) < t.calc(mid)) {
//现在t一定是比较不优的线段
Line tmp = t; t = line[rt]; line[rt] = tmp;
}
if (t.k < line[rt].k) {
//左端点还有贡献的机会,就往左端点
update(lson,t);
} else {
//如果右端点还有贡献的机会,就往右端点
update(rson,t);
}
}
} else {
int mid = l + r >> 1;
if (t.l <= mid) update(lson,t);
if (mid + 1 <= t.r) update(rson,t);
}
}
double query(int rt,int l,int r,int v) {
//查询在 v 点的最优势线段的取值
if (l == r) return line[rt].calc(v);
double ans = line[rt].calc(v); //当前区间的最优势线段在 v 点的取值
int mid = l + r >> 1;
if (v <= mid) ans = max(ans,query(lson,v));
else ans = max(ans,query(rson,v));
return ans;
}
}seg;
int main() {
seg.build(1,1,N);
scanf("%d",&q);
while (q--) {
scanf("%s",op);
if (op[0] == 'Q') {
int x; scanf("%d",&x);
printf("%d\n",(int) (seg.query(1,1,N,x) / 100));
} else {
double k,b; scanf("%lf%lf",&b,&k);
b -= k; //起点是 x = 1,b要减去 k
seg.update(1,1,N,Line(1,N,k,b));
}
}
return 0;
}