雖然題解很多,也有權值線段樹,但我的和他們似乎不盡相同,跑的也挺快。
所謂權值線段樹,就是用線段樹來儲存權值。
那什麼是權值呢?似乎小學初中學統計的時候了解到,他是描述數在資料中比例大小的量,這裡用作此數出現的次數。
做法顯然。
我們用\(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...