可持久化線段樹(主席樹)

2021-08-28 08:15:53 字數 3083 閱讀 3023

qwq我大概又是機房最後乙個學主席樹的了吧

其實之前一直都在講···只是沒做題

做了幾道以後發現都是乙個套路qwq關鍵就是能不能看出來要用主席樹

主要可以解決:

靜態/動態區間第k大(樹上也可以)

一些有關區間的帶某些限制的詢問(如出現次數等)

先把模板粘上來:

#include#include#include#include#includeusing namespace std;

const int maxn=200005+200000*18;

int n,m,a[maxn],root[maxn],tot;

int ls[maxn],rs[maxn],sum[maxn],b[maxn],p[maxn];

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

}inline void update(int &k,int pre,int l,int r,int c)

}inline int query(int u,int v,int l,int r,int k)

inline int rd()

while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();

return x*f;

}int main()

while(m--)

return 0;

}

然後是一些比較裸的題:

spoj10628

樹上的路徑也可以看做區間,每個節點的前乙個版本是父親節點,然後在query的時候稍作修改:

sum[x]+sum[y]-sum[lca]-sum[fa[lca]]

#include#include#include#include#include#define n 100005

#define m 2000005

using namespace std;

int n,m,cnt,head[n],a[n],b[n],num,dep[n],f[n][20];

int root[n],sum[m],ls[m],rs[m],tot;

inline int rd()

while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();

return x*f;

}struct edgeedge[n<<1];

inline void add(int x,int y)

inline int lca(int x,int y)

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

}inline void update(int &k,int pre,int l,int r,int p) return;

}inline int query(int u,int v,int lca,int fa,int l,int r,int k)

inline void dfs(int u,int fa) return;

}int main()

while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();

return x*f;

}inline void build(int &k,int l,int r)

}inline void update(int &k,int pre,int l,int r,int p) return;

}inline int query(int u,int v,int l,int r,int k)

int main()

return 0;

}

bzoj2653

讓區間中位數最大,套路地想二分答案去做

把小於mid的設成-1,大於等於的設成1,然後找最大區間和看是否》=0

但是這樣做複雜度**,考慮每次二分改變的1的個數總共不會多於n個

所以可以將數值排序後建主席樹,然後判斷時求區間max

#include#include#include#include#include#define n 20005

#define m 400005

#define ll long long

using namespace std;

int n,m,num,root[m],ls[m],rs[m],sum[m],lmx[m],rmx[m],tot;

int a[n],ans,q[5];

pairb[n];

inline int rd()

while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();

return x*f;

}inline void pushup(int x)

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

int mid=(l+r)>>1;

build(ls[k],l,mid); build(rs[k],mid+1,r);

pushup(k);

}inline void update(int &k,int pre,int l,int r,int p,int val)

int mid=(l+r)>>1;

if(p<=mid) update(ls[k],ls[pre],l,mid,p,val);

else update(rs[k],rs[pre],mid+1,r,p,val);

pushup(k); return;

}inline int query_all(int u,int l,int r,int l,int r)

inline int query_left(int u,int l,int r,int l,int r)

inline int query_right(int u,int l,int r,int l,int r)

inline bool check(int k,int a,int b,int c,int d)

int main()

printf("%d\n",ans=b[res].first);

} return 0;

}

主席樹(可持久化線段樹)

我真弱。連主席樹都不會。主席樹相當於多個線段樹,由於相鄰兩棵線段樹的節點的值只有少許不同,因此可以對於和前一棵樹一樣的子樹乙個指標指過去,無需操作,這樣每棵樹o logn 總複雜度o nlogn 以下是區間k大 include include include define n 100005 defi...

主席樹 可持久化線段樹

首先要學會普通的線段樹,然後理解權值線段樹,而主席樹就是多個權值線段樹 我自己的理解 但是這多個權值線段樹之間有公共部分,節約了空間。它一開始是乙個空樹,後來逐個添數,記錄新增的這個數在那個範圍內,並 1,顯然它每次只更新了一條鏈,其他不需要變,這樣就有了多個版本的線段樹。如果求 l,r 範圍內第k...

主席樹 可持久化線段樹

主席樹,即可持久化線段樹。可持久化 可以找到每次修改時的線段樹,即儲存了線段樹各個歷史版本,這樣就可以快速查詢到第 i 次修改前線段樹的狀態。核心思想 與歷史版本的線段樹共用部分結點。很明顯每次新建一棵線段樹帶來的時空消耗是難以承受的,但是可以發現,每次單點修改時,只會變動logn個結點 即從根結點...