學習筆記 權值線段樹

2022-04-30 01:45:09 字數 1877 閱讀 2580

雖然題解很多,也有權值線段樹,但我的和他們似乎不盡相同,跑的也挺快。

所謂權值線段樹,就是用線段樹來儲存權值。

那什麼是權值呢?似乎小學初中學統計的時候了解到,他是描述數在資料中比例大小的量,這裡用作此數出現的次數

做法顯然。

我們用\(cnt_i\)表示第\(i\)個數出現的次數,那麼可以這樣:

void update(int k)

void build(int k,int l,int r)

build(k<<1,l,mid),build(k<<1|1,mid+1,r);//向下遞迴

update(k);//動態更新點區間和

}

你是不是還在疑問這是在回答什麼問題?

對這就是詢問所有數中第\(x\)小值。

分析如何運用權值線段樹呢?

很簡單,我們每次查詢下左兒子的權值和\(sum\),如果\(x\geqslant sum\),就向右兒子遞迴詢問第\(x-sum\)小,否則向左兒子遞迴找第\(x\)值就好了。

有沒有發現紕漏?

這個陣列必須有單調性。

那就排序啊......

反正排序是\(o(n\log n)\)的,線段樹也是\(o(n\log n)\)的,不會成為瓶頸。

這裡先不給**了,下面再說。

我們想到線段樹是單調陣列,而使用是非常不便。

這就需要一種給力的對映關係

其實就是離散化了。

這裡就有**了:

struct node

t[maxn];

bool cmp(node n,node m)

考慮權值線段樹,加點並不好實現,不如強制離線,倒著試試。

刪除乙個數就十分簡單了,我們只需用原數到離散化陣列的對映關係,將此數對應的\(cnt--\)就好了,把答案存一下,逆序輸出來即可。

下面是簡單的單點修改:

void modify(int k,int x,int y)//現在在k點,目標是將x號搞成y

else if(x<=mid) modify(k<<1,x,y);

else modify(k<<1|1,x,y);

update(k);//仍然記得更新

}

總的說,這樣的複雜度是\(o((n+m)\log n)\)的,可以通過此題。

下面放上\(ac\)**:

#include#include#includeusing namespace std;

const int maxn=2e5+5;

struct node

t[maxn];

bool cmp(node n,node m)

build(k<<1,l,mid),build(k<<1|1,mid+1,r);

update(k);

}int query(int k,int x)

void modify(int k,int x,int y)

else if(x<=mid) modify(k<<1,x,y);

else modify(k<<1|1,x,y);

update(k);

}int n,m,l[maxn],now,ans[maxn];

int main()

for(int i=1;i<=m;i++) printf("%d\n",ans[i]);//將儲存的答案輸出

return 0;

}

這個版本的權值線段樹會跑\(900\;ms\),翻翻記錄,還挺快呢。

權值線段樹學習筆記

定義 struct segmenttree tree maxn 2 建樹 void build int p,int l,int r int mid l r 1 build lson,l,mid build rson,mid 1,r pushup 單點更新 void change int p,int ...

權值線段樹

維護全域性的值域資訊,每個節點記錄的是該值域的值出現的總次數。使用二分的思想 離散化的時候,需要用到 支援查詢全域性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...