動態開點線段樹
阿新 • • 發佈:2020-07-19
動態開點線段樹
前置芝士
眾所周知,普通線段樹空間複雜度是 \(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;
}
和普通線段樹一樣,可維護的東西很多,在這裡就不一一枚舉了,讀者自行理解。
提示
- 這個引數變數pos前面的取址符&是不能省略的,因為這個編號是不隨遞迴修改的定值
- 時間複雜度 \(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;
}
有問題請評論