poj 2104 K-th Number (划分树入门 或者 主席树入门)

题意:给n个数,m次询问,每次询问L到R中第k小的数是哪个

算法1:划分树

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 using namespace std;
 6 
 7 const int mx=1e5+5;
 8 int tree[30][mx];
 9 int sortt[mx];
10 int sum[30][mx];
11 
12 void build(int l,int r,int c)
13 {
14     if (l==r) return ;
15     int m=(l+r)>>1;
16     int isame=m-l+1;
17     int pl=l,pr=m+1;
18     for (int i=l;i<m;i++)
19         if (tree[c][i]<sortt[m]) isame--;
20     for (int i=l;i<=r;i++){
21         if (i==l) sum[c][i]=0;
22         else sum[c][i]=sum[c][i-1];
23         if (tree[c][i]<sortt[m]){
24             tree[c+1][pl++]=tree[c][i];
25             sum[c][i]++;
26         }
27         else if ( tree[c][i]==sortt[m]&&isame){
28             isame--;
29             tree[c+1][pl++]=tree[c][i];
30             sum[c][i]++;
31         }
32         else tree[c+1][pr++]=tree[c][i];
33     }
34     build(l,m,c+1);
35     build(m+1,r,c+1);
36 }
37 
38 int query(int L,int R,int l,int r,int c,int k){
39     if (L==R) return tree[c][L];
40     int s,ss;
41     if (l==L){
42         s=0;
43         ss=sum[c][r];
44     }
45     else{
46         s=sum[c][l-1];
47         ss=sum[c][r]-s;
48     }
49     int m=(L+R)>>1;
50     if (k<=ss) return query(L,m,L+s,L+s+ss-1,c+1,k);
51     return query(m+1,R,m+1+l-L-s,m+1+r-L-s-ss,c+1,k-ss);
52 }
53 
54 int main(){
55     int n,m;
56     scanf("%d%d",&n,&m);
57     for (int i=1;i<=n;i++){
58         scanf("%d",&sortt[i]);
59         tree[0][i]=sortt[i];
60     }
61     sort(sortt+1,sortt+1+n);
62     build(1,n,0);
63     while (m--)
64     {
65         int l,r,k;
66         scanf("%d%d%d",&l,&r,&k);
67         printf("%d
",query(1,n,l,r,0,k));
68     }
69 
70 }

 算法2:主席树

 1 /*************************************************************
 2 算法:主席树
 3 思想:建n+1棵线段树,每颗线段数结构都是一样,先建一棵空树,然
 4       后每棵树都在前一棵树的基础上进行修改。
 5       把样例按照代码模拟一遍,就很容易理解了。
 6 **************************************************************/
 7 #include<cstdio>
 8 #include<cstring>
 9 #include<algorithm>
10 #include<iostream>
11 #include<vector>
12 using namespace std;
13 
14 const int mx=1e5+5;
15 struct Tree        ///树的节点
16 {
17     int l,r;      ///树的区间
18     int ls,rs;    ///树的左右孩子
19     int sum;
20 };
21 Tree tree[mx*20];
22 int a[mx],b[mx];
23 int s[mx],cnt;
24 
25 void build(int l,int r,int &node)   ///建空树,和线段树差不多
26 {
27     node=++cnt;
28     tree[node].l=l;
29     tree[node].r=r;
30     tree[node].sum=0;
31     if (l==r) return ;
32     int m=(l+r)/2;
33     build(l,m,tree[node].ls);
34     build(m+1,r,tree[node].rs);
35 }
36 
37 void inser(int n1,int &n2,int pos)  ///在上一棵树的基础上建立新树
38 {
39     n2=++cnt;
40     tree[n2].l=tree[n1].l;     ///区间和上一棵树一样
41     tree[n2].r=tree[n1].r;
42     tree[n2].rs=tree[n1].rs;   ///保证没有修改的部分和上一棵树一样
43     tree[n2].ls=tree[n1].ls;
44     tree[n2].sum=tree[n1].sum+1;
45     if (tree[n1].l==tree[n1].r) return ;
46     int m=(tree[n1].l+tree[n1].r)/2;
47     if (pos<=m) inser(tree[n1].ls,tree[n2].ls,pos);
48     else inser(tree[n1].rs,tree[n2].rs,pos);
49 }
50 
51 int query(int n1,int n2,int k)    ///查询的k小的树
52 {
53     if (tree[n1].l==tree[n1].r)
54     {
55         return b[tree[n1].l];
56     }
57     int cut=tree[tree[n2].ls].sum-tree[tree[n1].ls].sum;
58     if (cut>=k) return query(tree[n1].ls,tree[n2].ls,k);
59     return query(tree[n1].rs,tree[n2].rs,k-cut);
60 }
61 
62 int main()
63 {
64     int n,m;
65     scanf("%d%d",&n,&m);
66     for (int i=1;i<=n;i++)
67     {
68         scanf("%d",&a[i]);
69         b[i]=a[i];
70     }
71     sort(b+1,n+b+1);
72     cnt=0;
73     build(1,n,s[0]);
74     for (int i=1;i<=n;i++)
75     {
76         int pos=lower_bound(b+1,b+n+1,a[i])-b; ///找的a[i]要放的位置
77         inser(s[i-1],s[i],pos);
78     }
79     while (m--)
80     {
81         int l,r,k;
82         scanf("%d%d%d",&l,&r,&k);
83         printf("%d
",query(s[l-1],s[r],k));
84     }
85 }
原文地址:https://www.cnblogs.com/pblr/p/5929049.html