主席树模板

简述:

  解决线段树无法求区间第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 }
View Code
原文地址:https://www.cnblogs.com/wuliking/p/11746374.html