主席树模板
Posted wuliking
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了主席树模板相关的知识,希望对你有一定的参考价值。
简述:
解决线段树无法求区间第k大的问题
代码:
1 ///主席树模版(查询区间第k大) 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #include <algorithm> 6 #include <cctype> 7 #define numm ch-48 8 #define pn putchar(‘ ‘) 9 #define pd putchar(‘ ‘) 10 //#define debug(args...) cout<<#args<<"->"<<args<<endl 11 //#define bug cout<<"*******************"<<endl 12 using namespace std; 13 template<typename T> 14 void read(T &res) { 15 char ch;bool flag=false; 16 while(!isdigit(ch=getchar()))(ch==‘-‘)&&(flag=true); 17 for(res=numm;isdigit(ch=getchar());res=(res<<1)+(res<<3)+numm); 18 flag&&(res=-res); 19 } 20 template<typename T> 21 void write(T x) { 22 if(x<0) x=-x,putchar(‘-‘); 23 if(x>9) write(x/10); 24 putchar(x%10+48); 25 } 26 typedef long long ll; 27 const int maxn=100010+10; 28 const int inf=0x3f3f3f3f; 29 struct bb { 30 int x,pos; 31 bool operator<(const bb& c) { 32 return x==c.x?pos<c.pos:x<c.x; 33 } 34 }b[maxn]; 35 struct node { 36 int l,r; ///当前结点的管辖范围 37 int zuo,you;///当前结点的左右儿子标号 38 int c; ///记录存在于这个区间的点的个数 39 }tree[maxn*40];///一般乘上nlogn就够用 40 int id,a[maxn],gen[maxn],first[maxn]; 41 ///id:每个树结点的标号 42 ///a[]:去重后且离散化后的数组 43 ///gen[]:树根结点的标号 44 void build(int l,int r) { 45 id++; 46 tree[id].c=0; 47 tree[id].l=l; 48 tree[id].r=r; 49 tree[id].zuo=tree[id].you=-1; 50 int now=id; ///attention!!! 51 if(l<r) { 52 int mid=l+r>>1; 53 tree[now].zuo=id+1;build(l,mid); ///now不要写成id 54 tree[now].you=id+1;build(mid+1,r); 55 } 56 } 57 void add(int x) { 58 int now=gen[x-1];gen[x]=id+1; 59 do { 60 int zuo=tree[now].zuo,you=tree[now].you; 61 ///zuo:now的左儿子标号,you同理 62 id++; 63 tree[id].c=tree[now].c+1;///插入一个数,比上一次多1 64 tree[id].l=tree[now].l; 65 tree[id].r=tree[now].r; 66 if(zuo==-1) {///当前是叶子结点 67 tree[id].zuo=tree[id].you=-1; 68 break; 69 } 70 if(a[x]<=tree[zuo].r) { ///大小在左子树范围内 71 tree[id].zuo=id+1; 72 tree[id].you=you;///沿用上一棵树的右子树 73 now=zuo;///递归左子树 74 } 75 else { ///同理 76 tree[id].zuo=zuo;///沿用上一棵树的左子树 77 tree[id].you=id+1; 78 now=you;///递归右子树 79 } 80 }while(now!=-1); 81 } 82 int found(int x,int y,int k) { 83 int l=gen[x-1],r=gen[y]; 84 while(tree[r].zuo!=-1) {///一直判到叶子结点 85 int zuo_sum=tree[tree[r].zuo].c-tree[tree[l].zuo].c; 86 int you_sum=tree[tree[r].you].c-tree[tree[l].you].c; 87 ///统计左右结点的数的个数 88 if(k<=zuo_sum) {///如果在左儿子里,就往左儿子走 89 l=tree[l].zuo; 90 r=tree[r].zuo; 91 } 92 else { ///如果在右儿子里,就往右儿子走 93 l=tree[l].you; 94 r=tree[r].you; 95 k-=zuo_sum; ///attention!!! 96 } 97 } 98 return tree[r].l; 99 } 100 int main() 101 { 102 int n,m; 103 read(n),read(m);///n个树,m个询问 104 for(int i=1;i<=n;i++) ///读入n个数 105 read(b[i].x),b[i].pos=i; 106 sort(b+1,b+1+n); 107 int cnt=0; 108 for(int i=1;i<=n;i++) { ///下面进行去重 109 if(i==1||b[i].x!=b[i-1].x) a[b[i].pos]=++cnt;///直接在a数组上修改 110 else a[b[i].pos]=cnt; 111 first[cnt]=b[i].x; ///记录这个序号对应的x 112 } 113 build(1,n); ///建树 114 gen[0]=1; ///由上一步得知,第一棵树的根标号是1 115 for(int i=1;i<=n;i++)///插入结点(建立剩下的n棵树) 116 add(i); 117 for(int i=1;i<=m;i++) { 118 int l,r,k; 119 read(l);read(r);read(k); 120 write(first[found(l,r,k)]);pn; 121 } 122 return 0; 123 }
以上是关于主席树模板的主要内容,如果未能解决你的问题,请参考以下文章