李超线段树简易讲解
大佬的博客讲的很清晰
李超线段树就是标记永久化维护区间线段最值的数据结构
假设有下列的问题:
给定平面中\(n\)条线段,(给出斜率\(k\)和截距\(b\),并且知道线段左右端点的横坐标值为多少),然后\(m\)个询问,每次给定\(x = x_0\),问和\(x = x_0\)相交的直线中,横坐标值最大为多少。
很明显\(n\)和\(m\)数据范围大了之后,我们无法暴力,此时看到区间端点,可以想能否用数据结构维护这个东西,即----李超线段树。
复杂度分析:
每次查询是\(logn\)的,
每次插入更新,我们可以把整个权值的域划分为\(logn\)个区间,这里是同一般更新的,但是每个区间内,我们要下放非优势线段,所以又套了一个\(log\),总体更新复杂度为\(log^2n\),但貌似常数比较小。
维护这个线段可以用结构体和数组实现,我个人偏好结构体一些,比较好写。
这里用结构体里面带\(flag\)来判断是否被标记过了,因为更新牵扯到优势线段的更新,不好操作,另开一个数组来维护
bool flag[N];
struct seg {
double k,b;
int l,r;
seg(){}
seg(double _k,double _b,int _l,int _r) {
k = _k,b = _b,l = _l,r = _r;
}
double gety(const int &pos) const {//返回某处的函数值
return k * pos + b;
}
double crossx(const seg &ts) const {//获得两个线段之间的交点
return (ts.b - b) / (k - ts.k);
}
}tree[N << 2];
更新过程主要是其实就是涉及到\(mid\)和端点处\(l,r\)几处的函数值讨论一下,即可完成,画图会更清晰的理解。
void modify(int rt,int l,int r,seg ins) {
int mid = (l + r) >> 1;
if(l >= ins.l && r <= ins.r) {
if(!flag[rt]) {//未被覆盖 直接更新
tree[rt] = ins; flag[rt] = true; return ;
}
if(ins.gety(l) - tree[rt].gety(l) > eps && ins.gety(r) - tree[rt].gety(r) > eps) {//当前插入的线段整体更优
tree[rt] = ins; return ;
}
if(ins.gety(l) - tree[rt].gety(l) > eps || ins.gety(r) - tree[rt].gety(r) > eps) {//任有一段大
if(ins.gety(mid) - tree[rt].gety(mid) > eps) swap(ins,tree[rt]);//tree保持的是当前最优线段
//画画图就理解了
if(ins.crossx(tree[rt]) - mid < -eps) modify(lson,l,mid,ins);
else modify(rson,mid+1,r,ins);
}
return ;
}
if(ins.l <= mid) modify(lson,l,mid,ins);
if(ins.r > mid) modify(rson,mid+1,r,ins);
}
查询就比较普通
double query(int rt,int l,int r,int pos) {
if(l == r) return tree[rt].gety(pos);
int mid = (l + r) >> 1;
double ans = tree[rt].gety(pos);
if(pos <= mid) ans = max(ans,query(lson,l,mid,pos));
else ans = max(ans,query(rson,mid+1,r,pos));
return ans;
}
到此就是大致的粗糙简易模板了
\(1.\)BZOJ 1568 裸的模板题
#include <bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-10;
const int ma = 50000;
const int N = 1e5 + 10;
int n;
char op[10];
struct seg {
double k,b;
int l,r,fg;
double gety(const int &pos) const {
return k * pos + b;
}
double crossx(const seg &a) const {
return (a.b - b) / (k - a.k);
}
}tree[N<<2];
void build(int rt,int l,int r) {
tree[rt].k = 0,tree[rt].b = 0,tree[rt].l = 1,tree[rt].r = ma;
if(l == r) return ;
int mid = (l + r) >> 1;
build(lson,l,mid); build(rson,mid+1,r);
}
void modify(int rt,int L,int R,seg k) {
if(L >= k.l && R <= k.r) {
// cout << rt << ' ' << tree[rt].fg << "****\n";
if(k.gety(L) - tree[rt].gety(L) > eps && k.gety(R) - tree[rt].gety(R) > eps) {
tree[rt] = k; return ;
}
if(k.gety(L) - tree[rt].gety(L) > eps || k.gety(R) - tree[rt].gety(R) > eps) {
int mid = (L + R) >> 1;
if(k.gety(mid) - tree[rt].gety(mid) > eps) swap(k,tree[rt]);//tree[rt] 存最优
if(k.crossx(tree[rt]) - mid < -eps) modify(lson,L,mid,k);
else modify(rson,mid+1,R,k);
}
return ;
}
int mid = (L + R) >> 1;
if(k.l <= mid) modify(lson,L,mid,k);
if(k.r > mid) modify(rson,mid+1,R,k);
}
double query(int rt,int l,int r,int pos) {
if(l == r) return tree[rt].gety(pos);
int mid = (l + r) >> 1;
double ans = tree[rt].gety(pos);
if(pos <= mid) return max(ans,query(lson,l,mid,pos));
else return max(ans,query(rson,mid+1,r,pos));
}
int main() {
scanf("%d",&n);
build(1,1,ma);
for(int i = 1;i <= n;i ++) {
scanf("%s",&op);
if(op[0] == 'P') {
double k,b;
scanf("%lf%lf",&b,&k);
seg tmp;
tmp.k = k,tmp.b = b - k,tmp.l = 1,tmp.r = ma;
modify(1,1,ma,tmp);
}
else if(op[0] == 'Q') {
int x;
scanf("%d",&x);
// cout << query(1,1,ma,x) << '\n';
int res = (int)(query(1,1,ma,x) / 100);
printf("%d\n",res);
}
}
return 0;
}
/*
100
Project 5.10200 0.65000
Project 2.76200 1.43000
Query 4
Query 2
Project 3.80200 1.17000
Query 2
Query 3
Query 1
Project 4.58200 0.91000
Project 5.36200 0.39000
*/
\(2.\)BZOJ3938 转换一下题意建立李超树模型
思路:对于每个机器人他距离原点的位置\(x\)其实是和时间\(t\)相关联的,也就是说每个机器人的位置其实就是\(x - t\)的一次函数图像,对应每个机器人的每段\(x - t\)图像都可变为一个线段,而又因为题目求得距离原点的距离最大值,所以我们就建两棵树分别维护整个区间上的最大值和最小值,最后取绝对值大者皆可。
时间范围大,离散化一下,存一下线段,基本变成了模板题。
这道题细节还是有的,注意别忘了存下来最开始的位置也当做线段,每个机器人最后一次操作和时间范围边界点也当做线段,两边都不要遗漏。
因为插入的时候,交点和\(mid\)大小符号判断写反了,debug了一天,都快wa吐了......
#include <bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-6;
const int N = 1e5 + 10;
const int M = 6e5 + 10;
bool flag[2][M<<2];
struct seg {
ll k,b;
int l,r;
seg(){}
seg(ll _k,ll _b,int _l,int _r) {
k = _k,b = _b,l = _l,r = _r;
}
ll gety(const ll &pos) const {
return k * pos + b;
}
ll crossx(const seg &ts) const {
return (ts.b - b) / (k - ts.k);
}
}tree[2][M << 2];
int n,m;
ll init[N],lsh[M];
int cnt;
struct Q {
ll v,ti;
int id;
char op[10];
}q[M];
void modifymax(int rt,int l,int r,seg ins) {
int mid = (l + r) >> 1;
if(l >= ins.l && r <= ins.r) {
if(!flag[0][rt]) {
tree[0][rt] = ins; flag[0][rt] = true; return ;
}
if(ins.gety(lsh[l]) > tree[0][rt].gety(lsh[l]) && ins.gety(lsh[r]) > tree[0][rt].gety(lsh[r])) {
tree[0][rt] = ins; return ;
}
if(ins.gety(lsh[l]) > tree[0][rt].gety(lsh[l]) || ins.gety(lsh[r]) > tree[0][rt].gety(lsh[r])) {
if(ins.gety(lsh[mid]) > tree[0][rt].gety(lsh[mid])) swap(ins,tree[0][rt]);
if(ins.crossx(tree[0][rt]) < lsh[mid]) modifymax(lson,l,mid,ins);
else modifymax(rson,mid+1,r,ins);
}
return ;
}
if(ins.l <= mid) modifymax(lson,l,mid,ins);
if(ins.r > mid) modifymax(rson,mid+1,r,ins);
}
void modifymin(int rt,int l,int r,seg ins) {
int mid = (l + r) >> 1;
if(l >= ins.l && r <= ins.r) {
if(!flag[1][rt]) {
tree[1][rt] = ins; flag[1][rt] = true; return ;
}
if(ins.gety(lsh[l]) < tree[1][rt].gety(lsh[l]) && ins.gety(lsh[r]) < tree[1][rt].gety(lsh[r])) {
tree[1][rt] = ins; return ;
}
if(ins.gety(lsh[l]) < tree[1][rt].gety(lsh[l]) || ins.gety(lsh[r]) < tree[1][rt].gety(lsh[r])) {
if(ins.gety(lsh[mid]) < tree[1][rt].gety(lsh[mid])) swap(ins,tree[1][rt]);
if(ins.crossx(tree[1][rt]) < lsh[mid]) modifymin(lson,l,mid,ins);
else modifymin(rson,mid+1,r,ins);
}
return ;
}
if(ins.l <= mid) modifymin(lson,l,mid,ins);
if(ins.r > mid) modifymin(rson,mid+1,r,ins);
}
ll res1,res2;
ll qmax(int rt,int l,int r,int pos) {
// if(flag[0][rt]) res1 = max(res1,tree[0][rt].gety(lsh[pos]));
if(l == r) return tree[0][rt].gety(lsh[pos]);
int mid = (l + r) >> 1;
ll ans = tree[0][rt].gety(lsh[pos]);
if(pos <= mid) ans = max(ans,qmax(lson,l,mid,pos));
else ans = max(ans,qmax(rson,mid+1,r,pos));
return ans;
}
ll qmin(int rt,int l,int r,int pos) {
// if(flag[1][rt]) res2 = min(res2,tree[1][rt].gety(lsh[pos]));
if(l == r) return tree[1][rt].gety(lsh[pos]);
int mid = (l + r) >> 1;
ll ans = tree[1][rt].gety(lsh[pos]);
if(pos <= mid) return min(ans,qmin(lson,l,mid,pos));
else return min(ans,qmin(rson,mid+1,r,pos));
}
bool vis[N];
ll lastk[N],lastb[N],lastt[N];
int getid(ll x) {
return lower_bound(lsh + 1,lsh + 1 + cnt,x) - lsh;
}
int main() {
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i ++) {
scanf("%lld",&lastb[i]);
}
for(int i = 1;i <= m;i ++) {
scanf("%lld%s",&q[i].ti,q[i].op);
lsh[++cnt] = q[i].ti;
if(q[i].op[0] == 'c') {
scanf("%d%lld",&q[i].id,&q[i].v);
}
}
lsh[++cnt] = 0;//用于插入初始值
sort(lsh + 1,lsh + 1 + cnt);
cnt = unique(lsh + 1,lsh + 1 + cnt) - lsh - 1;
for(int i = 1;i <= m;i ++) {
if(q[i].op[0] == 'c') {
int no = q[i].id;
// vis[no] = 1;
int l = getid(lastt[no]),r = getid(q[i].ti);
// cout << lastk[no] << ' ' << lastb[no] << ' ' << l << ' ' << r << "***\n";
modifymax(1,1,cnt,seg(lastk[no],lastb[no],l,r));
modifymin(1,1,cnt,seg(lastk[no],lastb[no],l,r));
lastb[no] = lastb[no] + q[i].ti * (lastk[no] - q[i].v);
lastk[no] = q[i].v;lastt[no] = q[i].ti;
}
}
for(int i = 1;i <= n;i ++) {
int l = getid(lastt[i]);
modifymax(1,1,cnt,seg(lastk[i],lastb[i],l,cnt));
modifymin(1,1,cnt,seg(lastk[i],lastb[i],l,cnt));
}
for(int i = 1;i <= m;i ++) {
if(q[i].op[0] == 'q') {
int no = q[i].id;
int time = getid(q[i].ti);
res1 = res2 = 0;
res1 = qmax(1,1,cnt,time); res2 = qmin(1,1,cnt,time);
// ll res1 = qmax(1,1,cnt,time); ll res2 = qmin(1,1,cnt,time);
// cout << res1 << ' ' << res2 << '\n';
ll res = max(res1,-res2);
printf("%lld\n",res);
}
}
return 0;
}
/*
4 5
-20 0 20 100
10 command 1 10
20 command 3 -10
30 query
40 command 1 -30
50 query
*/
数组标记线段实现:
#include <bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-6;
const int N = 1e5 + 10;
const int M = 6e5 + 10;
bool flag[2][M<<2];
ll tagk[2][M<<2],tagb[2][M<<2];
int n,m;
ll init[N],lsh[M];
int cnt;
struct Q {
ll v,ti;
int id;
char op[10];
}q[M];
ll cal(ll k,ll b,ll x) {
return k * x + b;
}
void modifymax(int rt,int l,int r,int L,int R,ll k,ll b) {
int mid = (l + r) >> 1;
if(l >= L && r <= R) {
if(!flag[0][rt]) {
tagk[0][rt] = k,tagb[0][rt] = b,flag[0][rt] = true; return ;
}
// if(cal(k,b,lsh[l]) > cal(tagk[0][rt],tagb[0][rt],lsh[l]) && cal(k,b,lsh[r]) > cal(tagk[0][rt],tagb[0][rt],lsh[r])) {
ll f1 = k * lsh[l] + b,f2 = tagk[0][rt] * lsh[l] + tagb[0][rt];
ll f3 = k * lsh[r] + b,f4 = tagk[0][rt] * lsh[r] + tagb[0][rt];
if(f1 <= f2 && f3 <= f4) return ;
if(f1 >= f2 && f3 >= f4) {
tagk[0][rt] = k,tagb[0][rt] = b; return ;
}
ll crossx = (tagb[0][rt] - b) / (k - tagk[0][rt]);
if(f1 >= f2) {
if(crossx <= lsh[mid]) modifymax(lson,l,mid,L,R,k,b);
else { modifymax(rson,mid+1,r,L,R,tagk[0][rt],tagb[0][rt]); tagk[0][rt] = k,tagb[0][rt] = b; }
}
else {
if(crossx > lsh[mid]) modifymax(rson,mid+1,r,L,R,k,b);
else { modifymax(lson,l,mid,L,R,tagk[0][rt],tagb[0][rt]); tagk[0][rt] = k,tagb[0][rt] = b; }
}
return ;
}
if(L <= mid) modifymax(lson,l,mid,L,R,k,b);
if(R > mid) modifymax(rson,mid+1,r,L,R,k,b);
}
void modifymin(int rt,int l,int r,int L,int R,ll k,ll b) {
int mid = (l + r) >> 1;
if(l >= L && r <= R) {
if(!flag[1][rt]) {
tagk[1][rt] = k,tagb[1][rt] = b,flag[1][rt] = true; return ;
}
ll f1 = k * lsh[l] + b,f2 = tagk[1][rt] * lsh[l] + tagb[1][rt];
ll f3 = k * lsh[r] + b,f4 = tagk[1][rt] * lsh[r] + tagb[1][rt];
if(f1 >= f2 && f3 >= f4) return ;
if(f1 <= f2 && f3 <= f4) {
tagk[1][rt] = k,tagb[1][rt] = b; return ;
}
ll crossx = (tagb[1][rt] - b) / (k - tagk[1][rt]);
if(f1 <= f2) {
if(crossx <= lsh[mid]) modifymin(lson,l,mid,L,R,k,b);
else { modifymin(rson,mid+1,r,L,R,tagk[1][rt],tagb[1][rt]); tagk[1][rt] = k,tagb[1][rt] = b; }
}
else {
if(crossx > lsh[mid]) modifymin(rson,mid+1,r,L,R,k,b);
else { modifymin(lson,l,mid,L,R,tagk[1][rt],tagb[1][rt]); tagk[1][rt] = k,tagb[1][rt] = b; }
}
return ;
}
if(L <= mid) modifymin(lson,l,mid,L,R,k,b);
if(R > mid) modifymin(rson,mid+1,r,L,R,k,b);
}
ll res1,res2;
void qmax(int rt,int l,int r,int pos) {
if(flag[0][rt]) res1 = max(res1,tagk[0][rt] * lsh[pos] + tagb[0][rt]);
if(l == r) return ;//cal(tagk[0][rt],tagb[0][rt],lsh[pos]);
int mid = (l + r) >> 1;
if(pos <= mid) qmax(lson,l,mid,pos);
else qmax(rson,mid+1,r,pos);
}
void qmin(int rt,int l,int r,int pos) {
if(flag[1][rt]) res2 = min(res2,tagk[1][rt] * lsh[pos] + tagb[1][rt]);
if(l == r) return ;
int mid = (l + r) >> 1;
if(pos <= mid) qmin(lson,l,mid,pos);
else qmin(rson,mid+1,r,pos);
}
bool vis[N];
ll lastk[N],lastb[N],lastt[N];
int getid(ll x) {
return lower_bound(lsh + 1,lsh + 1 + cnt,x) - lsh;
}
int main() {
scanf("%d%d",&n,&m);
ll ma = 0;
for(int i = 1;i <= n;i ++) {
scanf("%lld",&lastb[i]);
ma = max(ma,lastb[i]);
}
for(int i = 1;i <= m;i ++) {
scanf("%lld%s",&q[i].ti,q[i].op);
lsh[++cnt] = q[i].ti;
if(q[i].op[0] == 'c') {
scanf("%d%lld",&q[i].id,&q[i].v);
}
}
lsh[++cnt] = 0;//用于插入初始值
sort(lsh + 1,lsh + 1 + cnt);
cnt = unique(lsh + 1,lsh + 1 + cnt) - lsh - 1;
for(int i = 1;i <= m;i ++) {
if(q[i].op[0] == 'c') {
int no = q[i].id;
int l = getid(lastt[no]),r = getid(q[i].ti);
// cout << lastk[no] << ' ' << lastb[no] << ' ' << l << ' ' << r << "***\n";
modifymax(1,1,cnt,l,r,lastk[no],lastb[no]);
modifymin(1,1,cnt,l,r,lastk[no],lastb[no]);
lastb[no] = lastb[no] + q[i].ti * (lastk[no] - q[i].v);
lastk[no] = q[i].v;lastt[no] = q[i].ti;
}
}
for(int i = 1;i <= n;i ++) {
int l = getid(lastt[i]);
// cout << lastk[i] << ' ' << lastb[i] << ' ' << l << ' ' << cnt << "***\n";
modifymax(1,1,cnt,l,cnt,lastk[i],lastb[i]);
modifymin(1,1,cnt,l,cnt,lastk[i],lastb[i]);
}
for(int i = 1;i <= m;i ++) {
if(q[i].op[0] == 'q') {
int no = q[i].id;
int time = getid(q[i].ti);
res1 = res2 = 0;
qmax(1,1,cnt,time);qmin(1,1,cnt,time);
// ll res1 = qmax(1,1,cnt,time); ll res2 = qmin(1,1,cnt,time);
ll res = max(res1,-res2);
printf("%lld\n",res);
}
}
return 0;
}
/*
4 5
-20 0 20 100
10 command 1 10
20 command 3 -10
30 query
40 command 1 -30
50 query
*/