李超線段樹學習筆記

2021-09-24 18:00:49 字數 4212 閱讀 9269

至今不會李超線段樹\(.jpg\)……

先說明一下,李超線段樹只能解決「只插入」的問題,如果有刪除恕它無能為力

先考慮這麼乙個問題,我們要資瓷動態插入直線以及詢問直線\(x=k\)與其它所有直線相交的點中最大的\(y\)座標是多少

李超線段樹的具體過程是這樣的

對於乙個區間,我們維護該區間的所有直線中,從上往下去看可以看到它的長度最大的一條直線(即沒有被其他直線覆蓋的長度最大) (litble語)

我們插入一條直線的時候就需要分類討論了,以下稱插入直線為當前直線,之前的直線為記錄直線

1.如果沒有記錄直線,把記錄直線變成當前直線,返回

2.當前直線在這個區間中被記錄直線完全覆蓋,直接返回

3.當前直線在這個區間中完全覆蓋記錄直線,把記錄直線變成當前直線,返回

4.如果上面情況都不滿足,那麼這個區間中兩條直線顯然有交點,我們求出交點的\(x\)座標,根據\(x\)與區間中點的大小關係判斷哪條直線未被覆蓋的長一點,把記錄直線變成長一點的那個,另一條直線繼續遞迴

因為第四步中我們區間長度至少減少一半,所以它的複雜度是\(o(n\log n)\)的

這就是我們之前說的操作啦,直接上李超線段樹就行了

//minamoto

#include#define r register

#define fp(i,a,b) for(r int i=(a),i=(b)+1;ii;--i)

#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)

templateinline bool cmax(t&a,const t&b)

double readdb()

inline char getop()

char sr[1<<21],z[20];int c=-1,z=0;

inline void ot()

void print(r int x)

const int n=1e5+5;

struct node

inline double val(const r int &x)

}pool[n<<2],*rt;int tot;

inline node *newnode()

int n,x,q;double b,k,bb,kk,res;char op;

void build(node* &p,int l,int r)

void query(node *p,int l,int r)

void update(node *p,int l,int r)else

}int main()

return ot(),0;

}

//minamoto

#include#define r register

#define fp(i,a,b) for(r int i=(a),i=(b)+1;ii;--i)

#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)

templateinline bool cmax(t&a,const t&b)

char sr[1<<21],z[20];int c=-1,z=0;

inline void ot()

void print(r int x)

const int m=39989,l=1e9,n=1e5+5;

struct node

inline double calc(const r int &x)

}pool[n<<2],*rt;int tot;

int n,q,id,ii,ql,qr,res,x;double b,k,bb,kk,mx;

inline node *newnode()

void build(node* &p,int l,int r)

void update(node *p,int l,int r,double b,double k,int id)else

return;

}int mid=(l+r)>>1;

if(ql<=mid)update(p->lc,l,mid,b,k,id);

if(qr>mid)update(p->rc,mid+1,r,b,k,id);

}void query(node *p,int l,int r)

int cnt,op,val[n],id[n];

int main()

if(x0>x1)swap(x0,x1),swap(y0,y1);

k=1.0*(y1-y0)/(x1-x0),b=y0-k*x0,id=cnt,ql=x0,qr=x1;

update(rt,1,n,b,k,id);

}else

}return ot(),0;

}

如果我們把路徑拆成兩段,那麼這個路徑加可以看成是乙個一次函式

具體來說,設\(dis_u\)表示節點\(u\)到根節點的距離,那麼\((x,lca)\)這條路徑上每個節點的權值就會加上\(-dis_ua+dis_xa+b\),而\((lca,y)\)這條路徑上每個節點就會加上\(dis_ua+a(dis_x+2\times dis_)+b\)

區間加一次函式並維護最值,就是李超線段樹啦~~~~

我們把它給樹剖了,那麼同一條重鏈裡\(dis\)肯定是遞增的,我們就可以把插入直線變成插入線段

順便注意我們的線段樹上的節點是離散化之後的,所以在李超線段樹計算的時候要用原來的\(dis\)進行計算

樹剖乙個\(\log\),李超線段樹兩個\(\log\),總複雜度是\(o(n\log^3n)\)

//minamoto

#include#define r register

#define ll long long

#define inf 123456789123456789ll

#define fp(i,a,b) for(r int i=(a),i=(b)+1;ii;--i)

#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)

templateinline bool cmin(t&a,const t&b)

using namespace std;

char buf[1<<21],*p1=buf,*p2=buf;

inline char getc()

int read()

char sr[1<<21],z[20];int c=-1,z=0;

inline void ot()

void print(r ll x)

const int n=1e5+5;

struct ege[n<<1];int head[n],tot;

inline void add(r int u,r int v,r int w),head[u]=tot;}

ll dis[n],bb,kk;int dfn[n],rk[n],top[n],fa[n],sz[n],son[n],dep[n];

int n,m,cnt;

void dfs1(int u)

}void dfs2(int u,int t)

int lca(r int u,r int v)

inline ll calc(r ll x)

}pool[n<<2],*rt;int num;

inline node *newnode()

int ql,qr;ll res,k,b;

void build(node* &p,int l,int r)

void update(node *p,int l,int r,ll b,ll k)else

p->upd();

return;

}int mid=(l+r)>>1;

if(ql<=mid)update(p->lc,l,mid,b,k);

if(qr>mid)update(p->rc,mid+1,r,b,k);

p->upd();

}void query(node *p,int l,int r)

void change(int u,int v)

ql=dfn[v],qr=dfn[u],update(rt,1,n,b,k);

}void ask(int u,int v){

res=inf;

while(top[u]!=top[v]){

if(dep[top[u]]

李超線段樹學習筆記

至今不會李超線段樹 jpg 先說明一下,李超線段樹只能解決 只插入 的問題,如果有刪除恕它無能為力 先考慮這麼乙個問題,我們要資瓷動態插入直線以及詢問直線 x k 與其它所有直線相交的點中最大的 y 座標是多少 李超線段樹的具體過程是這樣的 對於乙個區間,我們維護該區間的所有直線中,從上往下去看可以...

李超線段樹學習筆記

李超線段樹支援這樣的操作 插入一條直線。詢問與 x x 0 相交的點的最大 小的縱座標 接下來我們以最大值為例 它基於線段樹的標記永久化,也就是說,我們對於區間 l,r 維護 x mid 上最高的直線的編號 但是如果 l,r 的所有點都以 l,r 的父親記錄的直線作為最大值,那麼 l,r 的子樹都沒...

李超線段樹

首先來看一道題 heoi2013 segment 可以發現的是,實質上某個 x k 處的最大值只有乙個,因此我們需要盡可能減少計算不優的線段。那麼對於兩條線段 a,b a ne b 它們左右端點橫座標相同,就只會產生如下四種情況 從特殊情況出發,每次我們都插入一條 1,n 的線段。如果是前兩條情況,...