目錄靜態字首第k小
靜態區間第k小
動態區間第k小
前置知識:值域線段樹,可持久化線段樹,樹狀陣列
題目:給定乙個序列和m次操作,每次操作修改單點或者詢問整個序列第k小的數
首先考慮暴力,對於每次修改都直接排序的話,複雜度為o(nmlogn),也可以魔改一下排序方法,不過一般的暴力還是沒辦法過
整體第k小帶修改很明顯可以用平衡樹做,不過程式設計較麻煩(而且大材小用),所以不考慮
值域線段樹
值域線段樹可以很方便的o(logn)查詢一次所有數中比某個值小的數的個數,於是我們可以考慮用它解決這一類問題,當然一般來說值域線段樹是和離散化配套使用的
做法:將所有數離散化後加入值域線段樹,修改操作就直接刪除舊的,加入新的
對於查詢操作,從根節點開始,當前節點的左兒子儲存著\(≤\)mid的數的個數sum,如果sum\(≥\)k,就說明第k小應該在左邊,遞迴到左兒子,否則k-=sum,遞迴給右兒子(k-=sum是因為在整個區間找第k小等價於在右區間找第k-sum小)
時間複雜度為o(nlogn),空間複雜度o(n*4)
題目:每次查詢前x個數中的第k小,無修改
做法:這裡改變一下上面的方法。上面的做法中,可以發現,sum的大小表示的是所有數中\(≤\)mid的數的個數,而這裡是要求前x個數中\(≤\)mid的數的個數,於是我們需要對每乙個數a[i]加入之後都對前i個數建立一顆值域線段樹,詢問前x個數的時候就使用第x個線段樹
可持續化線段樹
顯然不可能真的建立n個值域線段樹
鏈結時空複雜度o(nlogn)
題目:查詢改為[ l , r ]區間,無修改
上面的字首第k小相當於把主席樹看做字首和,那麼區間第k小相當於使用差分相減
首先明確一件事,對於上面建的n個值域線段樹(假裝把n個樹都單獨拆出來),形態完全相同,並且對於每乙個樹的相同位置,意義幾乎一樣,比如,第x個樹和第y個樹的某個位置都表示不大於c的數的個數,只不過乙個是針對前a[1x],另乙個a[1y]。所以可以考慮字首和的思想,假設y \(>\) x,用y樹乙個節點減去x樹上對應節點就可以表示a[ x+1 ~ y ]這一段上不大於c的數
做法:對於查詢[ l , r ],同時使用l-1和r兩個值域線段樹,每次的sum由r樹的左兒子減去l-1樹的左兒子得到,向下遞迴時兩個根要一起向同乙個方向走
時空複雜度o(nlogn)
code:
#include#define n 200005
using namespace std;
int n,m;
int ref[n],len;
int a[n],ndsum;
int root[n],ls[n*20],rs[n*20],sum[n*20];
template void read(t &x)
void build(int &rt,int l,int r)
void copynode(int x,int y)
int modify(int rt,int l,int r,int x,int val)
int query(int rt1,int rt2,int l,int r,int k)
int main()
for(int i=1;i<=m;++i)
return 0;
}
區間靜態第k小運用了字首和差分,所以顯然這個問題可以上樹
將乙個點到根節點的路徑建值域線段樹,對於父親\(rt\)和兒子\(v\),\(v\)的值域線段樹從\(rt\)擴充套件乙個點而來;查詢路徑\((u,v)\)用差分轉換成\(root[u]+root[v]-root[lca(u,v)]-root(fa[lca(u,v)])\)四棵線段樹加減
#include#define n 100005
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
typedef long long ll;
int n,m,a[n],b[n],len;
int f[n][18],dep[n];
int root[n],ndsum;
int sum[n*100],ls[n*100],rs[n*100];
struct edge
edge[n<<1];int head[n],cnt=1;
void add_edge(int from,int to)
template void read(t &x)
void build(int &rt,int las,int l,int r,int x)
void dfs(int rt,int fa)
}int lca(int x,int y)
int query(int rt1,int rt2,int rt3,int rt4,int l,int r,int k)
int main()
order[n];
template void read(t &x)
void modify(int &rt,int l,int r,int pos,int v)//修改某一顆樹
int pre_modify(int pos,int v)//修改logn顆樹
int query(int l,int r,int k)//詢問
else }
int main()
if(order[i].o=='c')
exc[++len]=order[i].val;
} sort(exc+1,exc+len+1);
len=unique(exc+1,exc+len+1)-exc-1;
for(int i=1;i<=n;++i) pre_modify(i,1);//加入a[i],建樹
for(int opt=1;opt<=m;++opt)
else
}return 0;
}
模板 區間第k小
我實在是太弱了現在才會這個東西qaq。主席樹做法。一張關於主席樹的無字說明 線段樹 2 是只單點修改了實心酒紅色點的線段樹 2 線段樹 2 中的藍色節點實際上就是線段樹 1 的藍色節點,我們只是把位址複製過來了。我們多開了乙個線段樹,但是節點數量卻只多了 log 層,那麼對於 n 的歷史版本保留就提...
主席樹(區間第k小)
k th number 求區間內第k小的數。主席樹的板子題 主席樹左子樹存小值,右邊大值,用sum記錄一下子樹節點個數。對 l,r 的查詢區間,root r root l 1 可得出 l,r 的差值,也就是大小的個數 include include include include include i...
主席樹 區間第k小
主席樹 權值線段樹 可持久化 權值線段樹 在此處指各個數字在某個區間內出現的次數 那麼第一棵權值線段樹會記錄 1,1 的數字出現次數 第n棵權值線段樹會記錄 1,n 的數字出現次數 例 數列為110001 第一棵權值線段樹記錄為tree1 0 0 tree1 1 1 第二棵權值線段樹記錄為tree2...