李超线段树合集

李超线段树
用来维护二维平面上很多条线段(直线)在x = x0上的最值问题
定义:
1.永久化标记:即线段树标记不删除,每个结点维护的也不一定是最优的信息,需要查询的时候一路统计标记达到最优
2.优势线段:李超树每个节点含有一个优势线段,意为完全覆盖当前区间且在当前区间mid处相比于其他该位置线段最大(小)的线段,李超树的id记录的即为当前的优势线段
3.核心思想:假设维护最大值,每次插入线段时,如果斜率较大的线段为优势线段,则斜率较小的线段只有在左子树才有机会比优势线段大
如果斜率较小的线段为优势线段,则较大的线段只有在右子树才能翻盘
修改(log(n))²(但是常数不大),查询log(n)
 

1.luogu4254

操作1.增加一条斜率为P,起始点为S的直线
操作2.询问k点上所有直线的最大值
#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <bitset>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)  
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))  
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x)  
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x)  
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long  
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second 
typedef vector<int> VI;
int read(){int x = 0,f = 1;char c = getchar();while (c<'0' || c>'9'){if (c == '-') f = -1;c = getchar();}
while (c >= '0'&&c <= '9'){x = x * 10 + c - '0';c = getchar();}return x*f;}
const double PI = acos(-1.0);
const double eps = 1e-9;
const int maxn = 5e4 + 10;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7; 
int N,M,K;
struct Tree{
    int l,r;
    double S,P;  //P为斜率,S为初始值 
}tree[maxn << 2];
void Build(int t,int l,int r){
    tree[t].l = l; tree[t].r = r;
    tree[t].S = tree[t].P = 0;
    if(l == r) return;
    int m = l + r >> 1;
    Build(t << 1,l,m); Build(t << 1 | 1,m + 1,r);
}
void update(int t,double S,double P){
    int mid = tree[t].l + tree[t].r >> 1;
    if(P > tree[t].P){
        swap(tree[t].P,P); swap(tree[t].S,S);
    }
    if(S + P * mid < tree[t].S + tree[t].P * mid){
        if(tree[t].l == tree[t].r) return;
        update(t << 1,S,P);
    }else{
        swap(tree[t].P,P); swap(tree[t].S,S);
        if(tree[t].l == tree[t].r) return;
        update(t << 1 | 1,S,P);
    }
}
double ans;
void query(int t,int k){
    int mid = tree[t].l + tree[t].r >> 1;
    ans = max(ans,k * tree[t].P + tree[t].S);
    if(tree[t].l == tree[t].r) return ;
    if(k <= mid) query(t << 1,k);
    else query(t << 1 | 1,k);
}
char op[10];
int main(){
    Sca(N);
    Build(1,1,50000);
    for(int i = 1; i <= N ; i ++){
        scanf("%s",op);
        if(op[0] == 'Q'){
            ans = 0;
            query(1,read());
            Pri((int)(ans / 100));
        }else{
            double S,P; scanf("%lf%lf",&S,&P);
            S -= P;
            update(1,S,P);
        }
    } 
    return 0;
}
View Code

 

2.luogu4097

  1. 在平面上加入一条线段。记第 i 条被插入的线段的标号为 i
  2. 给定一个数 k,询问与直线 x = k 相交的线段中,交点最靠上的线段的编号。
#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <bitset>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)  
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))  
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x)  
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x)  
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long  
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second 
typedef vector<int> VI;
int read(){int x = 0,f = 1;char c = getchar();while (c<'0' || c>'9'){if (c == '-') f = -1;c = getchar();}
while (c >= '0'&&c <= '9'){x = x * 10 + c - '0';c = getchar();}return x*f;}
const double PI = acos(-1.0);
const double eps = 1e-9;
const int maxn = 4e4 + 10;
const int maxm = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7; 
int N,M,K;
double S[maxm],P[maxm];
struct Tree{
    int l,r;
    int id;
}tree[maxn << 2];
void Build(int t,int l,int r){
    tree[t].l = l; tree[t].r = r;
    tree[t].id = 0;
    if(l == r) return;
    int m = l + r >> 1;
    Build(t << 1,l,m);
    Build(t << 1 | 1,m + 1,r);
}
bool dcmp(double a){
    return fabs(a) > eps;
}
void update(int t,int l,int r,int id){
    int m = (tree[t].l + tree[t].r) >> 1;
    if(l <= tree[t].l && tree[t].r <= r){
        int &a = id;
        int &b = tree[t].id;
        if(P[a] > P[b]) swap(a,b);
        if(m * P[a] + S[a] > m * P[b] + S[b] || (!dcmp(m * P[a] + S[a] - m * P[b] + S[b]) && a < b)){
            swap(a,b);
            if(tree[t].l == tree[t].r) return;
            update(t << 1 | 1,l,r,id);
        }else{
            if(tree[t].l == tree[t].r) return;
            update(t << 1,l,r,id);
        }
        return;
    }
    if(r <= m) update(t << 1,l,r,id);
    else if(l > m) update(t << 1 | 1,l,r,id);
    else{
        update(t << 1,l,m,id);
        update(t << 1 | 1,m + 1,r,id);
    }
}
int Id;
void query(int t,int k){
    int a = tree[t].id;
    double Ans = P[Id] * k + S[Id];
    if(Ans < P[a] * k + S[a] || (!dcmp(Ans - P[a] * k - S[a]) && Id > a)){
        Id = a;
    }
    if(tree[t].l == tree[t].r) return;
    int m = (tree[t].l + tree[t].r) >> 1;
    if(k <= m) query(t << 1,k);
    else query(t << 1 | 1,k);
}
int main(){
    Sca(N); Id = 0; int tot = 0;
    Build(1,1,40000);
    for(int i = 1; i <= N ; i ++){
        int op = read();
        if(!op){
            int k = (read() + Id - 1) % 39989 + 1;
            Id = 0; query(1,k);
            Pri(Id);
        }else{
            int x0 = (read() + Id - 1) % 39989 + 1;
            int y0 = (read() + Id - 1) % 1000000000 + 1;
            int x1 = (read() + Id - 1) % 39989 + 1;
            int y1 = (read() + Id - 1) % 1000000000 + 1;
            tot++;
            if(x0 == x1){
                P[tot] = 0;
                S[tot] = max(y1,y0);
            }else{
                P[tot] = 1.0 * (y1 - y0) / (x1 - x0);
                S[tot] = y1 - x1 * P[tot];    
            }
            update(1,min(x0,x1),max(x0,x1),tot);
        }
    }
    return 0;
}
View Code

 

3.luogu p4069

题意:树链上每个点添加形如a * dis + b的值的一个点,每次查询树链上最小点

树链剖分 + 李超树

关于李超树的区间查询:

用一个Min维护区间最小值,Min的组成来源为两个子树的Min的较小值和本结点优势线段在两个端点的较小值

如果查询包含整个区间则直接返回Min,否则来源为正常区间最小值查找 + 本结点优势线段在查询两端的较小值

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <bitset>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)  
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))  
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x)  
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x)  
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long  
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second 
typedef vector<int> VI;
int read(){int x = 0,f = 1;char c = getchar();while (c<'0' || c>'9'){if (c == '-') f = -1;c = getchar();}
while (c >= '0'&&c <= '9'){x = x * 10 + c - '0';c = getchar();}return x*f;}
const double PI = acos(-1.0);
const double eps = 1e-9;
const int maxn = 1e5 + 10;
const LL INF = 123456789123456789;
const int mod = 1e9 + 7; 
int N,M,K;
struct Edge{
    int to,next;
    LL dis;
}edge[maxn * 2];
int head[maxn],tot;
void init(){
    for(int i = 0 ; i <= N ; i ++) head[i] = -1;
    tot = 0;
}
void add(int u,int v,LL w){
    edge[tot].to = v;
    edge[tot].next = head[u];
    edge[tot].dis = w;
    head[u] = tot++;
}
int dep[maxn],top[maxn],fa[maxn],pos[maxn];
int sz[maxn],son[maxn],to[maxn];
LL dis[maxn];
void dfs1(int t,int la){
    sz[t] = 1; son[t] = t;
    int heavy = 0;
    for(int i = head[t]; ~i ; i = edge[i].next){
        int v = edge[i].to;
        if(v == la) continue;
        dis[v] = dis[t] + edge[i].dis;
        dep[v] = dep[t] + 1;
        fa[v] = t;
        dfs1(v,t);
        if(sz[v] > heavy){
            heavy = sz[v];
            son[t] = v;
        }
        sz[t] += sz[v];
    }
}
int cnt;
void dfs2(int t,int la){
    top[t] = la;
    pos[t] = ++cnt; to[cnt] = t;
    if(son[t] == t) return;
    dfs2(son[t],la);
    for(int i = head[t]; ~i ; i = edge[i].next){
        int v = edge[i].to;
        if((fa[t] == v) || (v == son[t])) continue;
        dfs2(v,v);
    }
}
LL S[maxn * 2],P[maxn * 2];
int ttt;
struct Tree{
    int l,r,id;
    LL Min;
}tree[maxn << 2];
void Pushup(int t){
    if(tree[t].l == tree[t].r) return;
    tree[t].Min = min(tree[t].Min,min(tree[t << 1].Min,tree[t << 1 | 1].Min));
}
void Build(int t,int l,int r){
    tree[t].l = l; tree[t].r = r;
    tree[t].id = 0; tree[t].Min = INF;
    if(l == r) return;
    int m = l + r >> 1;
    Build(t << 1,l,m); Build(t << 1 | 1,m + 1,r);
}
void update(int t,int l,int r,int id){
    int m = tree[t].l + tree[t].r >> 1;
    if(l <= tree[t].l && tree[t].r <= r){
        int &a = id; int &b = tree[t].id;
        if(P[a] > P[b]) swap(a,b);
        if(dis[to[m]] * P[a] + S[a] < dis[to[m]] * P[b] + S[b]){
            swap(a,b);
            if(tree[t].l != tree[t].r) update(t << 1,l,r,id);
        }else{
            if(tree[t].l != tree[t].r) update(t << 1 | 1,l,r,id);
        }
        Pushup(t);
        tree[t].Min = min(tree[t].Min,dis[to[tree[t].l]] * P[b] + S[b]);
        tree[t].Min = min(tree[t].Min,dis[to[tree[t].r]] * P[b] + S[b]);
        return;
    }
    if(r <= m) update(t << 1,l,r,id);
    else if(l > m) update(t << 1 | 1,l,r,id);
    else{
        update(t << 1,l,m,id);
        update(t << 1 | 1,m + 1,r,id);
    }
    Pushup(t);
}
LL query(int t,int l,int r){
    if(l <= tree[t].l && tree[t].r <= r) return tree[t].Min;
    int m = (tree[t].l + tree[t].r) >> 1;
    LL ans = INF;
    ans = min(ans,dis[to[l]] * P[tree[t].id] + S[tree[t].id]);
    ans = min(ans,dis[to[r]] * P[tree[t].id] + S[tree[t].id]);
    if(r <= m) return min(ans,query(t << 1,l,r));
    else if(l > m) return min(ans,query(t << 1 | 1,l,r));
    else{
        return min(ans,min(query(t << 1,l,m),query(t << 1 | 1,m + 1,r)));
    }
}
int lca(int x,int y){
    while(top[x] != top[y]) dep[top[x]] > dep[top[y]]?x = fa[top[x]]:y = fa[top[y]];
    return dep[x] < dep[y]?x:y;
}
void update(int u,int v,int id){
    while(top[u] != top[v]){
        if(dep[top[u]] < dep[top[v]]) swap(u,v);
        update(1,pos[top[u]],pos[u],id);
        u = fa[top[u]];
    }
    if(dep[u] > dep[v]) swap(u,v);
    update(1,pos[u],pos[v],id); 
}
LL query(int u,int v){
    LL ans = INF;
    while(top[u] != top[v]){
        if(dep[top[u]] < dep[top[v]]) swap(u,v);
        ans = min(ans,query(1,pos[top[u]],pos[u]));
        u = fa[top[u]];
    }
    if(dep[u] > dep[v]) swap(u,v);
    ans = min(ans,query(1,pos[u],pos[v]));
    return ans;
}
int main(){
    Sca2(N,M); init();
    P[0] = 0; S[0] = INF;
    for(int i = 1; i <= N - 1; i ++){
        int u = read(),v = read();
        LL w = read();
        add(u,v,w); add(v,u,w);
    }
    int root = 1;
    dfs1(root,-1); cnt = 0;
    dfs2(root,root);
    Build(1,1,N); ttt = 0;
    while(M--){
        int op = read();
        if(op == 1){
            int s = read(),t = read();
            LL a = read(),b = read();
            int x = lca(s,t);
            ttt++; P[ttt] = -a; S[ttt] = a * dis[s] + b; 
            update(s,x,ttt);
            ttt++; P[ttt] = a; S[ttt] = a * (dis[s] - 2 * dis[x]) + b;
            update(x,t,ttt);
        }else{
            int s = read(),t = read();
            Prl(query(s,t));
        }
    }
    return 0;
}
View Code

 

4.2019ICPC南京网络赛I

比赛的时候一直想寻找一个维护斜率最大值的数据结构,结果没有想到

看了题解才知道真有直接维护这个的数据结构

李超树的下标代表的是洗衣机花费的时间,每个x上的最大值是当前后缀需要花费的最大值时间

动态加入后缀 维护最大值即可

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <bitset>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)  
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))  
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x)  
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x)  
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long  
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second 
typedef vector<int> VI;
int read(){int x = 0,f = 1;char c = getchar();while (c<'0' || c>'9'){if (c == '-') f = -1;c = getchar();}
while (c >= '0'&&c <= '9'){x = x * 10 + c - '0';c = getchar();}return x*f;}
const double PI = acos(-1.0);
const double eps = 1e-9;
const int maxn = 1e6 + 10;
const LL INF = 1e18;
const int mod = 1e9 + 7; 
int N,M,K;
LL S[maxn],P[maxn];
LL a[maxn];
int cnt;
struct Tree{
    int l,r,id;
}tree[maxn << 2];
void Build(int t,int l,int r){
    tree[t].l = l; tree[t].r = r;
    tree[t].id = 0;
    if(l == r) return;
    int m = l + r >> 1;
    Build(t << 1,l,m);
    Build(t << 1 | 1,m + 1,r);
}
void update(int t,int id){
    int &a = id;int &b = tree[t].id;
    if(P[a] > P[b]) swap(a,b);
    int m = tree[t].l + tree[t].r >> 1;
    if(P[a] * m + S[a] > P[b] * m + S[b]){
        swap(a,b);
        if(tree[t].l != tree[t].r)update(t << 1 | 1,id);
    }else{
        if(tree[t].l != tree[t].r)update(t << 1,id);
    }
}
bool cmp(LL a,LL b){
    return a > b;
}
LL query(int t,int k){
    int a = tree[t].id;
    LL ans = P[a] * k + S[a];
    if(tree[t].l == tree[t].r) return ans;
    int m = tree[t].l + tree[t].r >> 1;
    if(k <= m) return max(query(t << 1,k),ans);
    else return max(query(t << 1 | 1,k),ans);
}
void add(int t){
    cnt++;
    S[cnt] = a[t]; P[cnt] = t;
    update(1,cnt);
}
LL ans[maxn];
int main(){
    while(~Sca2(N,M)){
        for(int i = 1; i <= N; i ++) Scl(a[i]); 
        S[0] = 0; P[0] = 0;
        sort(a + 1,a + 1 + N,cmp);
        Build(1,1,M); cnt = 0;
        int f = 1;
        for(int i = M; i >= 1; i --){
            while(f <= N && i * f <= M) add(f++);
            LL sum = query(1,i);
            if(f <= N) sum = max(sum,a[f] + M);
            ans[i] = sum;
        }
        for(int i = 1; i <= M ; i ++){
            printf("%lld",ans[i]);
            if(i != M) printf(" ");
        }
        puts("");
    }
    return 0;
}
View Code

 

posted @ 2019-09-02 23:27  Hugh_Locke  阅读(455)  评论(0编辑  收藏  举报