1. 程式人生 > 實用技巧 >動態開點線段樹

動態開點線段樹

動態開點線段樹

前置芝士

眾所周知,普通線段樹空間複雜度是 \(O(n*4)\)

所以當n很大的時候,如果正常的去建一顆線段樹,開4倍n空間顯然會炸記憶體

怎麼辦呢?

這個時候,動態開點線段樹出現了。

概念

​ 動態開點線段樹是一類特殊的線段樹,與普通的線段樹不同的是,每一個節點的左右兒子不是該點編號的兩倍和兩倍加一,而是現加出來的。

​ 簡單的說,就是建立一棵“殘疾”的線段樹,上面只有詢問過的相關節點。

​ 大概長這樣(借的圖,自己畫的太醜了),理解即可,寫出來的可能不長這樣

作用

​ 一般有兩種:

  • 節約空間,所以在需要時再建兒子。
  • 運用主席樹的時候(此文章不會提及)。

程式碼

更新
inline void push_up(int o){
    sum[o]=sum[ls[o]]+sum[rs[o]];
}

ls[o],rs[o]代表lson和rson,以下都一樣

插入
inline void update(int &o,int l,int r,int pos,int val){	//o為當前樹的編號 l r為區間 pos為插入位置 val為插入大小
    if(!o) o=++cnt;//開點
    if(l==r){
        sum[o]+=val;
        return ;
    }
    int mid=(l+r)>>1;
    if(x<=mid) update(ls[o],l,mid,pos,val);
    else update(rs[o],mid+1,r,pos,val);
    push_up(o);//更新
}
查詢
inline int ask(int o,int l,int r,int L,int R){
    if(!o) return 0;//沒有這個點,直接返回0
    if(L<=l&&r<=R) return sum[o];
    int val,mid=(l+r)>>1;
    if(L<=mid) val+=ask(ls[o],l,mid,L,R);
    if(R>mid) val+=ask(rs[o],mid+1,r,L,R);
    return val;
}

和普通線段樹一樣,可維護的東西很多,在這裡就不一一枚舉了,讀者自行理解。

提示
  1. 這個引數變數pos前面的取址符&是不能省略的,因為這個編號是不隨遞迴修改的定值
  2. 時間複雜度 \(O(\dfrac{q}{logn})~~~~q\) 為查詢次數,\(n\) 為值域

例題

P1908 逆序對
題意

就是求一個序列的逆序對個數

思路

相信大家都做過這題,所以就不說思路了,只是把歸併排序改為了動態開點線段樹

程式碼

有註釋的

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define lson l,mid,tree[rt].l
#define rson mid+1,r,tree[rt].r
#define ls tree[rt].l
#define rs tree[rt].r

const int mac=5e5+10;
const int inf=1e9+5;

struct node
{
    int l,r,sum;
}tree[mac*32];
int sz=1;//動態分配的點的最大編號

ll query(int l,int r,int rt,int L,int R)
{
    ll ans=0;
    if (l>=L && r<=R){
        return tree[rt].sum;
    }
    int mid=(l+r)>>1;
    if (mid>=L) ans+=query(lson,L,R);
    if (mid<R) ans+=query(rson,L,R);
    return ans;
}

void update(int l,int r,int &rt,int pos)
{
    if (!rt) rt = ++sz;//如果說這個節點不存在,我們就將節點數+1,當前節點為最大最大節點,和主席樹有點類似,而這也是動態開點的核心
    if (l==r) {
        tree[rt].sum++;
        return;
    }
    int mid=(l+r)>>1;
    if (mid>=pos) update(lson,pos);//注意這裡傳進去的rt是左兒子的編號
    else update(rson,pos);
    tree[rt].sum=tree[ls].sum+tree[rs].sum;
}

int main()
{
    int n;
    scanf ("%d",&n);
    ll ans=0;
    int root=1;
    for (int i=1; i<=n; i++){
        int x;
        scanf ("%d",&x);
        ans+=query(1,inf,1,x+1,inf);
        update(1,inf,root,x);
    }
    printf ("%lld\n",ans);
    return 0;
}

有問題請評論