普通主席树
普通主席树比较简单 就是很多个权值线段树 每一次加进去log个节点(每层一个),剩下的节点用原来的线段树中的节点直接连到新节点上就好了
线段树
插入节点
主席树
主席树拆开之后
POJ2104 主席树模板题 代码:
#include
#include
using namespace std;
typedef long long ll;
ll read(){
ll x=0,f=1;char c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
const int maxn=100100,maxm=5050;
int n,m,cnt,nums;
int a[maxn],b[maxn],num[maxn],root[maxn];
vector vec;
struct Node{
int l,r,val;
} tr[maxn*40];
int getid(int x){return int(lower_bound(vec.begin(),vec.end(),x)-vec.begin())+1;}
void update(int l,int r,int &x,int y,int val){
nums++;tr[nums]=tr[y];tr[nums].val++;x=nums;
if(l==r) return;
int md=(l+r)>>1;
if(val<=md) update(l,md,tr[x].l,tr[y].l,val);
else update(md+1,r,tr[x].r,tr[y].r,val);
}
int ask(int l,int r,int x,int y,int val){
if(l==r) return l;
int md=(l+r)>>1;
int nw=tr[tr[y].l].val-tr[tr[x].l].val;
if(val<=nw) return ask(l,md,tr[x].l,tr[y].l,val);
else return ask(md+1,r,tr[x].r,tr[y].r,val-nw);
}
int main(){
#ifdef LZT
//freopen("in","r",stdin);
#endif
n=read(),m=read();
for(int i=1;i<=n;i++)
a[i]=read(),vec.push_back(a[i]);
sort(vec.begin(),vec.end());vec.erase(unique(vec.begin(),vec.end()),vec.end());
for(int i=1;i<=n;i++)
a[i]=getid(a[i]),update(1,n,root[i],root[i-1],a[i]);
for(int i=1;i<=m;i++){
int l=read(),r=read(),k=read();
printf("%d\n",vec[ask(1,n,root[l-1],root[r],k)-1]);
}
return 0;
}j
/*
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
*/
可修改主席树
其实就是树状数组套主席树
为什么不能直接修改呢?
因为主席树的第i棵树是被第i+1到n棵树包含的
那么每次修改就得把后面的所有的树全部修改一遍
如果运气不好 复杂度可能达到$O(mn \log n)
那么我们需要一个每个点可以通过log的时间算出来并且修改也只需log的时间的东西----->树状数组
ZOJ2112 Dynamic Rankings
这是一道可修改主席树的模板题
目前并没有看出来这个数据结构有什么应用 但是感觉很有用的样子
好像也可以cdq分治+整体二分做
这题比较卡内存 所以初值单独记录在一个线段树中 修改记录在树状数组中 查询的时候综合一下
时间复杂度: O(M×logn×logn+nlogn)
代码:
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include
#include
#include
#include
#include
#include