主席树再探

匿名 (未验证) 提交于 2019-12-02 23:03:14

(零基础者出门左拐)
最近学了主席树,打了几道模板题。
感觉还行
主席树,在我看来就是线段树的可持化 (一开始以为主席树只是可持久化权值线段树)。在题目中需要建多颗线段树或权值线段树且,相邻的线段树差别不大(一般就一个点不一样)时就可以用主席树。运用可持久化的思想,我们并不需要重新构建一颗线段树,因为只需要改一个点,所以线段树只需要新多出\(logn\)个节点,其他的节点继承前面的线段树就行了(所以一般都要开始建一颗空树)。这样一来,我们建树的时间复杂度和、这一堆线段树的空间复杂度变成了\(nlogn\),真是佩服人类的智慧。

嗯,模板题,求区间第k大,我们找到对应的两颗线段树\(root[r]\)\(root[l-1]\)然后在这两颗线段数上同时二分就行了。

#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int N=2e5+100; int num,n,m,a[N],b[N],tot,root[N],w[N*20],ch[N*20][2]; void build(int l,int r,int &now){     now=++num;     if(l==r)return;     int mid=(l+r)>>1;     build(l,mid,ch[now][0]);     build(mid+1,r,ch[now][1]); } void ins(int l,int r,int x,int pre,int &now){     now=++num;     w[now]=w[pre]+1;     if(l==r)return;     ch[now][0]=ch[pre][0];     ch[now][1]=ch[pre][1];     int mid=(l+r)>>1;     if(x>mid)ins(mid+1,r,x,ch[pre][1],ch[now][1]);     else ins(l,mid,x,ch[pre][0],ch[now][0]); } int check(int l,int r,int k,int pre,int now){     if(l==r)return l;     int tmp=w[ch[now][0]]-w[ch[pre][0]];     int mid=(l+r)>>1;     if(tmp>=k)return check(l,mid,k,ch[pre][0],ch[now][0]);     else return check(mid+1,r,k-tmp,ch[pre][1],ch[now][1]); } int read(){     int sum=0,f=1;char ch=getchar();     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}     while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}     return sum*f; } int main(){     n=read();m=read();     for(int i=1;i<=n;i++)a[i]=read(),b[++tot]=a[i];     sort(b+1,b+1+tot);     tot=unique(b+1,b+1+tot)-b-1;     for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+1+tot,a[i])-b;     build(1,tot,root[0]);     for(int i=1;i<=n;i++)ins(1,tot,a[i],root[i-1],root[i]);     while(m--){         int l=read(),r=read(),k=read();         printf("%d\n",b[check(1,tot,k,root[l-1],root[r])]);     }     return 0; }

非常短

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!