POJ 2104 第K大的数, 主席树,

    第一次接触这种神奇的数据结构,感觉不错。有学了个好东西,也不难。他主要应该是针对于数据统计的,例如本题的第k大的数。算法的主要思想是 先对给定的数离散化,然后在线段树中保存数字出现的次数(即叶子节点会存该节点所对应的数字出现的次数,非叶结点则保存子节点的数字之和)(这就与我们普通的线段树不同了)。然后我们会对于 从 a1 到 ai ( 1<=i<=n )之间的数据建一棵线段树,因此总共会建n+1棵,(看到这,你会不会担心MLE呢?其实不会的,因为节点是可以共用的)。 建完后,如果 查询是针对 ai 到 aj 这个区间的,那么我们就可以用aj那棵线段树上的值减去 ai-1 那棵线段树上的值,那么就可以得到在这个区间中各个数字出现的次数。加油!

真奇怪,为什么 POJ 上写read(),读入优化反而慢了700多毫秒。求大神赐教。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 #define rep(i,j,k) for(int i = j; i <= k; i++)
 5 using namespace std;
 6 int root[100005], a[100005], p[100005];
 7 int n_nodes = 0, n_numb;
 8 
 9 struct node{
10 int times, lc, rc;
11 } nod[3000000];
12 
13 void build(int l,int r,int&p){
14     p = ++n_nodes;
15     nod[p].lc = nod[p].rc = nod[p].times = 0;
16     if( l >= r ) return;
17     int mid = (l+r)/2;
18     build(l,mid,nod[p].lc); build(mid+1,r,nod[p].rc);
19 }
20 
21 void build2(int l,int r,int&p, int pre,int mre)
22 {
23     p = ++n_nodes;
24     nod[p] = nod[pre];
25     nod[p].times++;
26     if( l >= r ) return;
27     int mid = (l+r)/2;
28     if( mre <= mid ){
29         build2(l,mid,nod[p].lc,nod[pre].lc,mre);
30     }    
31     else {
32         build2(mid+1,r,nod[p].rc,nod[pre].rc,mre);
33     }
34 }
35 
36 int ask(int l,int r,int pre,int p,int k)
37 {
38     if( l >= r ) return l;
39     int s = nod[nod[p].lc].times - nod[nod[pre].lc].times;
40     int mid = (l+r) / 2;
41     if( s >= k ){
42         return ask(l,mid,nod[pre].lc,nod[p].lc,k);
43     }
44     else {
45         return ask(mid+1,r,nod[pre].rc,nod[p].rc,k-s);
46     }
47 }
48 
49 int main()
50 {
51     int n, q;
52     scanf("%d %d",&n,&q);
53     rep(i,1,n){
54         scanf("%d", &a[i]);
55         p[i] = a[i];
56     }
57     sort(p+1,p+1+n);
58     n_numb = unique(p+1,p+1+n) - p - 1;
59     build(1,n_numb,root[0]);
60     rep(i,1,n){
61         int m = lower_bound(p+1,p+n_numb+1,a[i]) - p;
62         build2(1,n_numb,root[i],root[i-1],m);
63     }
64     rep(i,1,q){
65         int x, y, k;
66         scanf("%d %d %d",&x,&y,&k);
67         int m = ask(1,n_numb,root[x-1],root[y],k);
68         printf("%d
", p[m]);
69     }
70     return 0;
71 }
人一我十,人十我万!追逐青春的梦想,怀着自信的心,永不放弃!仿佛已看到希望,尽管还在远方
原文地址:https://www.cnblogs.com/83131yyl/p/5085520.html