線段樹咕咕咕
我來寫乙個好寫的權值線段樹的解析吧
權值線段樹是什麼
線段樹每個點維護的是點的值,而權值線段樹葉子點維護的是乙個數出現的次數,父節點維護的是它代表的區間裡的數出現的次數的和
權值線段樹基本操作
其實權值線段樹的基本操作與線段樹沒有太大的不同
建樹
注意:由於乙個葉子節點代表乙個數出現的次數,所以,在數的範圍非常大時,我們常常需要把這些數排個序,按排序重新賦值(因為權值線段樹里數本身的值不重要,我們只需要關注它出現的次數),這也叫做離散化
void build(int l,int r,intro)}
新增
就是找到代表這個數的葉子節點,將它的權值++
void add(int x,intro)}
查詢出現次數
例題:luogu p1903 逆序對
查詢和線段樹的查詢操作一樣,
若節點代表的區間與要查詢的區間對應,返回它的權值
若要查詢的區間處於節點左子節點的區間內,去它的左子節點查詢
若要查詢的區間處於節點右子節點的區間內,去它的右子節點查詢
若要查詢的區間橫跨節點左右子節點的範圍,那分別去左右子節點查詢,並加和
int find(int l,int r,intro)}
查詢第k大的數
例題:luogu p3332 [zjoi2013]k大數查詢(這是我在洛谷裡能找到的有關係的題)
查詢第k大的數,也就相當於查詢第kk(kk = n- k + 1)小的數
若kk<節點左子節點的範圍,在左子節點裡找就好了
若kk>節點左子節點的範圍,說明這個數在右子節點裡,到右子節點找,但是,需要注意的是,kk的值要減去左子節點的範圍,代表我們要在右子樹里找第kk - tr[zuo].w + 1小的數
若查詢到葉子節點,那葉子節點代表的數即為所求
int numk(int l,int r,int ro,intk)}
貼乙個40分逆序對**
#include#include#include
using
namespace
std;
struct
ina[
500005
];struct
node
tr[4000005
];void build(int l,int r,int
ro)}
int find(int l,int r,int
ro)}
void add(int x,int
ro)}
bool cmp(in x,in
y)bool cmpp(in x,in
y)int
main()
sort(a + 1,a + 1 +n,cmp);
for(int i = 1;i <= n;i ++)
sort(a + 1,a + 1 +n,cmpp);
build(
1,n,1
);
for(int i = 1;i <= n;i ++)
printf("%d
",ans);
return0;
}
歡迎大佬指出錯誤啊
權值線段樹
維護全域性的值域資訊,每個節點記錄的是該值域的值出現的總次數。使用二分的思想 離散化的時候,需要用到 支援查詢全域性k小值,全域性rank,前驅,後繼等。單詞操作時間複雜度為o logn 空間複雜度為o n 相對於平衡樹的優勢 簡單,速度快 劣勢 值域較大時,我們需要離散化,變成離線資料結構 我認為...
權值線段樹
include using namespace std int n,m,tre 10003 4 laz 10003 4 void pushdown int num void update int num,int le,int ri,int x,int y,int z pushdown num int...
權值線段樹
權值線段樹是線段樹的一種,但是它與線段樹不同 線段樹的每個結點是用來維護一段區間的最大值或總和 而權值線段樹的每個結點儲存的一段區間有多少個數 權值線段樹主要用來查詢區間第k大或者第k小的值 現在有乙個陣列x 10 對陣列排序後為x 10 每個數的個數如下 1 32 2 3 24 1 5 18 1 ...