普通主席樹可以查詢區間k小值,但若直接修改,則複雜度極大,而可修改主席樹通過樹狀陣列的輔助來修改,大大縮小了時間複雜度,缺點是空間複雜度過大.
修改的時間複雜度為o((logn)^2),空間複雜度為o(n*logn^2).
為了減小修改複雜度,可以修改每個區間管理的範圍.
傳統主席樹每個點對應的主席樹管理乙個字首,而可修改主席樹因為樹狀陣列在處理動態區間和上有優勢,每個點管理的範圍就是它在樹狀陣列中的管理範圍.
具體而言,在加入元素時,對所有在樹狀陣列中包含它的主席樹都進行修改,而刪除也是同乙個道理.
而再查詢時與樹狀陣列類似,樹狀陣列的查詢是log的點的相加,這裡也是一樣,log個點一起在主席樹上同時同向跳動.
log個點跳log次,故每次的複雜度為log^2.
在建樹時,為了能盡可能多的共用節點,可以先建一棵根為0,什麼都不管理的主席樹,並將所有其他主席樹的根節點先初始化為這棵主席樹的根節點,最後將元素一一加入即可.
而因為有修改操作,所以在離散化前,要先將問題儲存起來,將修改後的變數一起離散化.
(以洛谷 p2617 dynamic ranking為例)
#include
#include
#include
#include
#define c ch=getchar()
#define lb(x) (x&(-x))
#define mid ((l+r)>>1)
#define n 10010
using
namespace
std;
int m,n,size,tt,num[n],root[n],b[n*2],bb,c[n*2],jia[n],jian[n],ja,jn;
struct tree
int left,right,left_son,right_son,gs;
} tree[n*400];
struct cz
cz[n];
mapid;
void build(int now,int l,int r)
}void ad(int now,int l,int r,int u)
tree[++tt]=tree[tree[now].right_son];
tree[now].right_son=tt;
ad(tt,mid+1,r,u);
}}inline
void add(int u,int v)
}void de(int now,int u)
de(tree[now].right_son,u);
}inline
void del(int u,int v)
}int as(int u)
for(i=1; i<=jn; i++)
if(u<=sum)
for(i=1; i<=jn; i++)
return as(u);
}for(i=1; i<=ja; i++)
for(i=1; i<=jn; i++)
return as(u-sum);
}inline
int ask(int u,int v,int w)
for(i=v; i; i-=lb(i))
return as(w);
}inline
void check(int now)
int main()
bb=n;
for(i=1; i<=m; i++)
sort(b+1,b+bb+1);
for(i=1; i<=bb; i++)
}root[0]=++tt;
build(tt,1,size);
for(i=1; i<=n; i++)
for(i=1; i<=n; i++)
// check(root[1]);
for(i=1; i<=m; i++)
if(cz[i].ch=='c')
}}
可修改主席樹 樹上可修改主席樹 樹套樹套樹
題目 思路 其實樹上主席樹是把每一棵主席樹看做樹狀陣列上的乙個點,每次修改log棵主席樹,求區間和的時候同樣log查詢字首和。上 include define d while d isdigit ch getchar using namespace std const int n 2e4 5 int...
Data 帶修改的主席樹 樹狀陣列套主席樹
樹狀陣列套主席樹 樹狀陣列的每個節點維護的是一段區間,我們將每個區間構造成一棵線段樹,這時候如果我們要修改乙個值,只需要修改logn個節點即可,時間複雜度為log 2 n 樹狀陣列維護的區間是數的個數n 離散化時是把所有數 包括要修改的數 全部離散化 1.修改 在修改之前,我們應先把序列裡原來的值在...
luogu P2617 樹狀陣列套主席樹
給定乙個含有n個數的序列a 1 a 2 a 3 a n 程式必須回答這樣的詢問 對於給定的i,j,k,在a i a i 1 a i 2 a j 中第k小的數是多少 1 k j i 1 並且,你可以改變一些a i 的值,改變後,程式還能針對改變後的a繼續回答上面的問題。你需要編乙個這樣的程式,從輸入檔...