Loading

数据结构模板

不包括字符串和图论内容。
代码压行警告qwq

如果存在与数据结构有关的经典算法,也会予以列出。

1. 单调队列 \(O(n)\)

luoguP1886

单调队列和单调栈的讲解在这里

const int maxn=1000010;
int n,k,cnt,a[maxn],minans[maxn],maxans[maxn];
deque<int> minint,maxint;
void push(int pos)
{
    while(!minint.empty()&&minint.front()<=pos-k)minint.pop_front();
    while(!maxint.empty()&&maxint.front()<=pos-k)maxint.pop_front();
    while(!minint.empty()&&a[minint.back()]>=a[pos])minint.pop_back();
    while(!maxint.empty()&&a[maxint.back()]<=a[pos])maxint.pop_back();
    minint.push_back(pos);maxint.push_back(pos);
}
//......
for(int i=1;i<k;i++)push(i);
for(int i=k;i<=n;i++)
{
    push(i);cnt++;
    minans[cnt]=a[minint.front()];
    maxans[cnt]=a[maxint.front()];
}

2. 单调栈 \(O(n)\)

luoguP5788

const int maxn=3000010;
int n,a[maxn],ans[maxn];
stack<int> st;

for(int i=n;i>=1;i--)
{
    while(!st.empty()&&a[st.top()]<=a[i])st.pop();
    if(!st.empty())ans[i]=st.top();
    st.push(i);
}

3. 并查集 \(O(n\alpha(n))\)

luoguP3367

路径压缩+启发式合并
玄学的时间复杂度分析

const int maxn=100010;
int n,fa[maxn],siz[maxn];
void init(int xx){for(int i=1;i<=xx;i++){fa[i]=i;siz[i]=1;}}
int find(int xx){return fa[xx]==xx?xx:fa[xx]=find(fa[xx]);}
int query(int xx,int yy){return find(xx)==find(yy);}
void merge(int xx,int yy)
{
    int fx=find(xx),fy=find(yy);
    if(fx==fy)return;
    if(siz[fx]<siz[fy]){fa[fx]=fy;siz[fy]+=siz[fx];fa[xx]=fy;}
    else{fa[fy]=fx;siz[fx]+=siz[fy];fa[yy]=fx;}
}

4. ST表 \(O(n\log n)-O(1)\)

luoguP3865

对ST表和树状数组的简单讲解

int n,m,ans,l,r,k,lg2[100010],st[100010][20];
for(int i=2;i<100010;i++)lg2[i]=lg2[i/2]+1;
for(int i=1;i<=n;i++)scanf("%d",&st[i][0]);
for(int j=1;j<=lg2[n];j++)
	for(int i=1;i+(1<<(j-1))<=n;i++)
		st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
for(int i=1;i<=m;i++)
{
	scanf("%d%d",&l,&r);k=lg2[r-l+1];
	ans=max(st[l][k],st[r-(1<<k)+1][k]);
	printf("%d\n",ans);
}

5. 树状数组

5.1 树状数组模板 \(O(n\log n)-O(\log n)\)

luoguP3374

预处理可以\(O(n)\),但没必要。

int n,m,a[500010];
inline int lowbit(int x){return x&(-x);}
inline void add(int x,int k){while(x<=n)a[x]+=k,x+=lowbit(x);}
inline int query(int pos)
{
    int ans=0;
    while(pos)ans+=a[pos],pos-=lowbit(pos);
    return ans;
}

5.2 树状数组求逆序对 \(O(n\log n)\)

luoguP1908

struct node{int pos,x;}a[500010];
int rk[500010];
bool cmp(node xx,node yy)
{
    if(xx.x!=yy.x)return xx.x<yy.x;
    return xx.pos<yy.pos;
}
class Bittree
{
public:
    int num=500010,datas[500010];
    int lowbit(int x){return x&(-x);}
    void add(int x,int k){while(x<=num)datas[x]+=k,x+=lowbit(x);}
    int query(int pos)
    {
        int ans=0;
        while(pos)ans+=datas[pos],pos-=lowbit(pos);
        return ans;
    }
}tree;
int n,ans;
//......
for(int i=1;i<=n;i++){scanf("%lld",&a[i].x);a[i].pos=i;}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)rk[a[i].pos]=i;
for(int i=1;i<=n;i++){tree.add(rk[i],1);ans+=i-tree.query(rk[i]);}

6. 线段树

6.1 复杂标记线段树 \(O(n)-O(\log n)\)

luoguP3373为例。
高度模式化的代码。

const int maxn=100010;
int n,m,p,a[maxn];
struct node{int l,r,val,add,mul;}tree[maxn<<2];
void pushup(int x)
{
    int lson=x<<1,rson=lson|1;
    tree[x].val=(tree[lson].val+tree[rson].val)%p;
}
void pushmul(int x,int k)
{
    tree[x].val*=k;tree[x].val%=p;
    tree[x].mul*=k;tree[x].mul%=p;
    tree[x].add*=k;tree[x].add%=p;
}
void pushadd(int x,int k)
{
    tree[x].val+=k*(tree[x].r-tree[x].l+1);tree[x].val%=p;
    tree[x].add+=k;tree[x].add%=p;
}
void pushdown(int x)
{
    int lson=x<<1,rson=lson|1;
    if(tree[x].l==tree[x].r)return;
    if(tree[x].mul!=1)//if的先后是按标记的优先级排的
    {
        pushmul(lson,tree[x].mul);
        pushmul(rson,tree[x].mul);
        tree[x].mul=1;
    }
    if(tree[x].add!=0)
    {
        pushadd(lson,tree[x].add);
        pushadd(rson,tree[x].add);
        tree[x].add=0;
    }
}
void build(int x,int l,int r)
{
    tree[x]=(node){l,r,0,0,1};
    if(l==r){tree[x].val=a[l];return;}
    int mid=(tree[x].l+tree[x].r)>>1,lson=x<<1,rson=lson|1;
    build(lson,l,mid);build(rson,mid+1,r);
    pushup(x);
}
void modify_mul(int x,int l,int r,int k)
{
    pushdown(x);
    if(l<=tree[x].l&&r>=tree[x].r){pushmul(x,k);return;}
    int mid=(tree[x].l+tree[x].r)>>1,lson=x<<1,rson=lson|1;
    if(l<=mid)modify_mul(lson,l,r,k);
    if(r>mid)modify_mul(rson,l,r,k);
    pushup(x);
}
void modify_add(int x,int l,int r,int k)
{
    pushdown(x);
    if(l<=tree[x].l&&r>=tree[x].r){pushadd(x,k);return;}
    int mid=(tree[x].l+tree[x].r)>>1,lson=x<<1,rson=lson|1;
    if(l<=mid)modify_add(lson,l,r,k);
    if(r>mid)modify_add(rson,l,r,k);
    pushup(x);
}
int query_sum(int x,int l,int r)
{
    pushdown(x);
    if(l<=tree[x].l&&r>=tree[x].r)return tree[x].val;
    int mid=(tree[x].l+tree[x].r)>>1,lson=x<<1,rson=lson|1,ans=0;
    if(l<=mid){ans+=query_sum(lson,l,r);ans%=p;}
    if(r>mid){ans+=query_sum(rson,l,r);ans%=p;}
    return ans;
}

6.2 动态开点权值线段树 \(O(n\log k)\)

luoguP3369
有些询问可以用set/map模拟,但这样写确实是原汁原味的权值线段树。
加强版卡空间过不去qaq

const int top=1e7+1;//根据值域进行相应改动
int n,cnt,rt;
struct node{int l,r,val;}tree[5000010];//看值域开,别MLE就行
void modify(int &k,int x,int l,int r,int add)
{
    if(!k)k=++cnt;
    tree[k].val+=add;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(x<=mid)modify(tree[k].l,x,l,mid,add);
    else modify(tree[k].r,x,mid+1,r,add);
}
int query(int k,int treel,int treer,int l,int r)
{
    if(!k)return 0;
    if(l<=treel&&r>=treer)return tree[k].val;
    int mid=(treel+treer)>>1,ans=0;
    if(l<=mid)ans+=query(tree[k].l,treel,mid,l,r);
    if(r>mid)ans+=query(tree[k].r,mid+1,treer,l,r);
    return ans;
}
int getnum(int k,int x,int l,int r)
{
    if(!k)return 0;
    if(l==r)return l;
    int mid=(l+r)>>1;
    if(tree[tree[k].l].val>=x)return getnum(tree[k].l,x,l,mid);
    else return getnum(tree[k].r,x-tree[tree[k].l].val,mid+1,r);
}
int getrank(int x){return query(rt,1,2e7+1,1,x-1)+1;}//这里的询问也是根据值域进行改动
int getpre(int x){return getnum(rt,getrank(x)-1,1,2e7+1);}
int getsuc(int x){return getnum(rt,getrank(x+1),1,2e7+1);}

6.3 扫描线

7. 树链剖分

7.1 轻重链剖分 \(O(n\log^2 n)\)

luoguP3384

const int maxn=100010;
int n,m,rt,p,cnt,dfncnt,a[maxn],w[maxn],h[maxn],dep[maxn],siz[maxn],son[maxn],fa[maxn],top[maxn],dfn[maxn];
struct node{int l,r,val,add;}tree[maxn<<2];
void pushup(int x)
{
    int lson=x<<1,rson=lson|1;
    tree[x].val=(tree[lson].val+tree[rson].val)%p;
}
void pushadd(int x,int k)
{
    tree[x].val+=k%p*(tree[x].r-tree[x].l+1)%p;tree[x].val%=p;
    tree[x].add+=k;tree[x].add%=p;
}
void pushdown(int x)
{
    int lson=x<<1,rson=lson|1;
    if(tree[x].l==tree[x].r)return;
    if(tree[x].add!=0)
    {
        pushadd(lson,tree[x].add);
        pushadd(rson,tree[x].add);
        tree[x].add=0;
    }
}
void build(int x,int l,int r)
{
    tree[x]=(node){l,r,0,0};
    if(l==r){tree[x].val=a[l]%p;return;}
    int mid=(l+r)>>1,lson=x<<1,rson=lson|1;
    build(lson,l,mid);build(rson,mid+1,r);
    pushup(x);
}
void modify(int x,int l,int r,int k)
{
    pushdown(x);
    if(l<=tree[x].l&&r>=tree[x].r){pushadd(x,k);return;}
    int mid=(tree[x].l+tree[x].r)>>1,lson=x<<1,rson=lson|1;
    if(l<=mid)modify(lson,l,r,k);
    if(r>mid)modify(rson,l,r,k);
    pushup(x);
}
int query(int x,int l,int r)
{
    pushdown(x);
    if(l<=tree[x].l&&r>=tree[x].r)return tree[x].val%p;
    int mid=(tree[x].l+tree[x].r)>>1,lson=x<<1,rson=lson|1,ans=0;
    if(l<=mid){ans+=query(lson,l,r);ans%=p;}
    if(r>mid){ans+=query(rson,l,r);ans%=p;}
    return ans;
}
struct edge{int to,nxt;}e[maxn<<1];
void addedge(int u,int v){e[++cnt]=(edge){v,h[u]};h[u]=cnt;}
void add(int u,int v){addedge(u,v);addedge(v,u);}
void dfs1(int u,int f)
{
    fa[u]=f;siz[u]++;dep[u]=dep[f]+1;
    int maxsiz=0;
    for(int i=h[u];i;i=e[i].nxt)
    {
        int p=e[i].to;
        if(p!=f)
        {
            dfs1(p,u);siz[u]+=siz[p];
            if(maxsiz<siz[p]){maxsiz=siz[p];son[u]=p;}
        }
    }
}
void dfs2(int u,int f)
{
    top[u]=f;dfn[u]=++dfncnt;
    if(w[u])modify(1,dfn[u],dfn[u],w[u]);
    if(!son[u])return;
    dfs2(son[u],f);
    for(int i=h[u];i;i=e[i].nxt)
    {
        int p=e[i].to;
        if(p!=son[u]&&p!=fa[u])dfs2(p,p);
    }
}
int lca(int u,int v)//这个操作是模板题所没有的,但是轻重链剖分也资瓷,是O(nlogn)的
{
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v])swap(u,v);
    return u;
}
void addpath(int u,int v,int k)
{
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        modify(1,dfn[top[u]],dfn[u],k%p);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v])swap(u,v);
    modify(1,dfn[u],dfn[v],k);
}
int querypath(int u,int v)
{
    int ans=0;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        ans+=query(1,dfn[top[u]],dfn[u]);ans%=p;
        u=fa[top[u]];
    }
    if(dep[u]>dep[v])swap(u,v);
    ans+=query(1,dfn[u],dfn[v]);ans%=p;
    return ans;
}
void addtree(int u,int k){modify(1,dfn[u],dfn[u]+siz[u]-1,k%p);}
int querytree(int u){return query(1,dfn[u],dfn[u]+siz[u]-1);}

7.2 dsu on tree/静态链分治

CF600E

7.3 长链剖分

8. 分块相关

见这里

9. 左偏树

luoguP3377
对于这道题,左偏树需要与并查集一起使用。

10. 平衡树

10.1 普通平衡树 \(O(n\log n)\)

luoguP6136
快进到数据加强版QwQ
讲道理这个隐藏的数据范围是真的坑人……

10.1.1 fhq_treap版

const int maxn=2000010;
int tot,rt;
struct node{int l,r,val,key,siz;}tree[maxn];
inline void update(int x){tree[x].siz=tree[tree[x].l].siz+tree[tree[x].r].siz+1;}
inline void newnode(int &x,int val){x=++tot;tree[x]=(node){0,0,val,rand(),1};}
inline void split(int x,int val,int &rx,int &ry)
{
    if(!x){rx=ry=0;return;}
    if(tree[x].val<=val){rx=x;split(tree[x].r,val,tree[x].r,ry);}
    else{ry=x;split(tree[x].l,val,rx,tree[x].l);}
    update(x);
}
inline int merge(int x,int y)
{
    if(!x||!y)return x+y;
    if(tree[x].key<tree[y].key){tree[x].r=merge(tree[x].r,y);update(x);return x;}
    else{tree[y].l=merge(x,tree[y].l);update(y);return y;}
}
void ins(int val)
{
    int now,x,y;
    split(rt,val,x,y);newnode(now,val);
    rt=merge(merge(x,now),y);
}
void del(int val)
{
    int x,y,z;
    split(rt,val,x,z);split(x,val-1,x,y);
    y=merge(tree[y].l,tree[y].r);
    rt=merge(merge(x,y),z);
}
int getrank(int val)
{
    int rk,x,y;split(rt,val-1,x,y);
    rk=tree[x].siz+1;rt=merge(x,y);
    return rk;
}
int getnum(int rk)
{
    int now=rt;
    while(now)
    {
        int lsiz=tree[tree[now].l].siz;
        if(lsiz+1==rk)break;
        else if(rk<=lsiz)now=tree[now].l;
        else{rk-=lsiz+1;now=tree[now].r;}
    }
    return tree[now].val;
}
int getpre(int val){return getnum(getrank(val)-1);}
int getsuc(int val){return getnum(getrank(val+1));}
//...
srand(19260817);//玄学数字

10.1.2 splay版

const int maxn=2000010;
int tot,rt;
struct node{int fa,ch[2],val,cnt,siz;}tree[maxn];
inline bool dir(int x,int f){return tree[f].ch[1]==x;}
inline void con(int x,int f,int s){tree[f].ch[s]=x;tree[x].fa=f;}
inline void upd(int x){tree[x].siz=tree[tree[x].ch[0]].siz+tree[tree[x].ch[1]].siz+tree[x].cnt;}
inline void newnode(int &x,int f,int val){x=++tot;tree[x]=(node){f,{0,0},val,1,1};}
inline void rotate(int x)
{
    int f=tree[x].fa,g=tree[f].fa,k=dir(x,f);
    con(tree[x].ch[k^1],f,k);con(x,g,dir(f,g));con(f,x,k^1);
    upd(f);upd(x);
}
inline void splay(int x,int top)
{
    if(!top)rt=x;
    while(tree[x].fa!=top)
    {
        int f=tree[x].fa,g=tree[f].fa;
        if(g!=top)dir(x,f)^dir(f,g)?rotate(x):rotate(f);
        rotate(x);
    }
}
void ins(int val,int &now,int fa)
{
    if(!now){newnode(now,fa,val);splay(now,0);}
    else if(val<tree[now].val)ins(val,tree[now].ch[0],now);
    else if(val>tree[now].val)ins(val,tree[now].ch[1],now);
    else{tree[now].cnt++;splay(now,0);}
}
void delnode(int x)
{
    splay(x,0);
    if(tree[x].cnt>1)tree[x].cnt--;
    else if(tree[x].ch[1])
    {
        int now=tree[x].ch[1];
        while(tree[now].ch[0])now=tree[now].ch[0];
        splay(now,x);con(tree[x].ch[0],now,0);
        rt=now;tree[now].fa=0;upd(rt);
    }
    else{rt=tree[x].ch[0];tree[rt].fa=0;}
}
void del(int val,int now)
{
    if(val==tree[now].val)delnode(now);
    else if(val<tree[now].val)del(val,tree[now].ch[0]);
    else del(val,tree[now].ch[1]);
}
int getrank(int val)
{
    int now=rt,rk=1;
    while(now)
    {
        int lsiz=tree[tree[now].ch[0]].siz;
        if(val==tree[now].val){rk+=lsiz;splay(now,0);break;}
        else if(val<tree[now].val)now=tree[now].ch[0];
        else{rk+=lsiz+tree[now].cnt;now=tree[now].ch[1];}
    }
    return rk;
}
int getnum(int rk)
{
    int now=rt;
    while(now)
    {
        int lsiz=tree[tree[now].ch[0]].siz;
        if(lsiz+1<=rk&&rk<=lsiz+tree[now].cnt){splay(now,0);break;}
        else if(rk<=lsiz)now=tree[now].ch[0];
        else{rk-=lsiz+tree[now].cnt;now=tree[now].ch[1];}
    }
    return tree[now].val;
}
int getpre(int x){return getnum(getrank(x)-1);}
int getsuc(int x){return getnum(getrank(x+1));}

10.2 文艺平衡树

10.2.1 fhq_treap版

const int maxn=100010;
int tot,rt;
struct node{int l,r,val,key,siz,rev;}tree[maxn];
inline void pushup(int x){tree[x].siz=tree[tree[x].l].siz+tree[tree[x].r].siz+1;}
inline void newnode(int &x,int val){x=++tot;tree[x]=(node){0,0,val,rand(),1,0};}
inline void pushdown(int x)
{
    swap(tree[x].l,tree[x].r);
    tree[tree[x].l].rev^=1;
    tree[tree[x].r].rev^=1;
    tree[x].rev=0;
}
inline void split(int x,int siz,int &rx,int &ry)
{
    if(!x){rx=ry=0;return;}
    if(tree[x].rev)pushdown(x);
    if(tree[tree[x].l].siz<siz){rx=x;split(tree[x].r,siz-tree[tree[x].l].siz-1,tree[x].r,ry);}
    else{ry=x;split(tree[x].l,siz,rx,tree[x].l);}
    pushup(x);
}
inline int merge(int x,int y)
{
    if(!x||!y)return x+y;
    if(tree[x].key<tree[y].key)
    {
        if(tree[x].rev)pushdown(x);
        tree[x].r=merge(tree[x].r,y);pushup(x);return x;
    }
    else
    {
        if(tree[y].rev)pushdown(y);
        tree[y].l=merge(x,tree[y].l);pushup(y);return y;
    }
}
void ins(int pos,int val)
{
    int now,x,y;
    split(rt,pos,x,y);newnode(now,val);
    rt=merge(merge(x,now),y);
}
void modify_rev(int l,int r)
{
    int x,y,z;
    split(rt,l-1,x,y);
    split(y,r-l+1,y,z);
    tree[y].rev^=1;
    rt=merge(merge(x,y),z);
}
void tour(int x)
{
    if(!x)return;
    if(tree[x].rev)pushdown(x);
    tour(tree[x].l);
    printf("%d ",tree[x].val);
    tour(tree[x].r);
}

10.2.2 splay版

const int maxn=100010;
int n,m,tot,rt;
struct node{int fa,ch[2],pos,val,cnt,siz,rev;}tree[maxn];
inline bool dir(int x,int f){return tree[f].ch[1]==x;}
inline void con(int x,int f,int s){tree[f].ch[s]=x;tree[x].fa=f;}
inline void pushup(int x){tree[x].siz=tree[tree[x].ch[0]].siz+tree[tree[x].ch[1]].siz+tree[x].cnt;}
inline void newnode(int &x,int f,int pos,int val){x=++tot;tree[x]=(node){f,{0,0},pos,val,1,1,0};}
inline void rotate(int x)
{
    int f=tree[x].fa,g=tree[f].fa,k=dir(x,f);
    con(tree[x].ch[k^1],f,k);con(x,g,dir(f,g));con(f,x,k^1);
    pushup(f);pushup(x);
}
inline void splay(int x,int top)
{
    if(!top)rt=x;
    while(tree[x].fa!=top)
    {
        int f=tree[x].fa,g=tree[f].fa;
        if(g!=top)dir(x,f)^dir(f,g)?rotate(x):rotate(f);
        rotate(x);
    }
}
inline void pushdown(int x)
{
    swap(tree[x].ch[0],tree[x].ch[1]);
    tree[tree[x].ch[0]].rev^=1;
    tree[tree[x].ch[1]].rev^=1;
    tree[x].rev=0;
}
void ins(int pos,int val,int &now,int fa)
{
    if(!now){newnode(now,fa,pos,val);splay(now,0);}
    else if(pos<tree[now].pos)ins(pos,val,tree[now].ch[0],now);
    else if(pos>tree[now].pos)ins(pos,val,tree[now].ch[1],now);
    else{tree[now].cnt++;splay(now,0);}
}
int getnum(int rk)
{
    int now=rt;
    while(1)
    {
        if(tree[now].rev)pushdown(now);
        int lsiz=tree[tree[now].ch[0]].siz;
        if(rk==lsiz+1)return now;
        else if(rk<=lsiz)now=tree[now].ch[0];
        else{rk-=lsiz+1;now=tree[now].ch[1];}
    }
}
void modify_rev(int l,int r)
{
    int x=getnum(l),y,z=getnum(r+2);
    splay(x,0);splay(z,x);
    y=tree[tree[rt].ch[1]].ch[0];tree[y].rev^=1;
}
void tour(int x)
{
    if(!x)return;
    if(tree[x].rev)pushdown(x);
    tour(tree[x].ch[0]);
    if(tree[x].pos!=0&&tree[x].pos!=n+1)printf("%d ",tree[x].val);
    tour(tree[x].ch[1]);
}
//Splay维护时注意前后插入pos为0和n+1的结点

11. 可持久化数据结构

11.1 可持久化数组 \(O(n\log n)\)

luoguP3919

const int maxn=1000010;
int n,cnt,rt[maxn],a[maxn];
struct node{int l,r,val;}tree[maxn*40];
void build(int &x,int l,int r)
{
    x=++cnt;
    if(l==r){tree[x].val=a[l];return;}
    int mid=(l+r)>>1;
    build(tree[x].l,l,mid);
    build(tree[x].r,mid+1,r);
}
void modify(int &k,int ver,int x,int l,int r,int val)
{
    k=++cnt;tree[k]=tree[ver];
    if(l==r){tree[k].val=val;return;}
    int mid=(l+r)>>1;
    if(x<=mid)modify(tree[k].l,tree[ver].l,x,l,mid,val);
    else modify(tree[k].r,tree[ver].r,x,mid+1,r,val);
}
int query(int k,int x,int l,int r)
{
    if(l==r)return tree[k].val;
    int mid=(l+r)>>1;
    if(x<=mid)return query(tree[k].l,x,l,mid);
    else return query(tree[k].r,x,mid+1,r);
}

11.2 主席树

const int maxn=200010;
int n,m,tot,cnt,a[maxn],val[maxn],rt[maxn];
map<int,int> mp;
struct node{int l,r,sum;}tree[maxn*40];
void build(int &x,int l,int r)
{
	x=++tot;
	if(l==r)return;
	int mid=(l+r)>>1;
	build(tree[x].l,l,mid);
	build(tree[x].r,mid+1,r);
}
void modify(int &k,int last,int x,int l,int r)
{
	k=++tot;
	tree[k]=(node){tree[last].l,tree[last].r,tree[last].sum+1};
	if(l==r)return;
	int mid=(l+r)>>1;
	if(x<=mid)modify(tree[k].l,tree[last].l,x,l,mid);
	else modify(tree[k].r,tree[last].r,x,mid+1,r);
}
int query(int k,int last,int l,int r,int rk)
{
	if(l==r)return l;
	int mid=(l+r)>>1;
	int lsum=tree[tree[k].l].sum-tree[tree[last].l].sum;
	if(lsum>=rk)
		return query(tree[k].l,tree[last].l,l,mid,rk);
	else
		return query(tree[k].r,tree[last].r,mid+1,r,rk-lsum);
}

11.3 可持久化并查集 \(O(n\log^2 n)\)

luoguP3402

不路径压缩+按秩合并的并查集。
就是个可持久化数组的简单应用。
时间复杂度口胡:
考虑将节点按照完全二叉树的方式进行合并。
则此时因为只有按秩合并,树高显然为\(\log n\)级别的。
然后不停地query高度最小的节点,于是find函数也需要\(\log n\)次递归,而可持久化数组的单次查询操作是\(O(\log n)\)的。
则find一次的时间复杂度为\(O(\log^2 n)\),总的时间复杂度为\(O(n\log^2 n)\)

之前的写假了,正在调


11.4 可持久化平衡树

11.5 可持久化文艺平衡树

posted @ 2021-07-08 23:05  pjykk  阅读(94)  评论(0编辑  收藏  举报