第一次接触这种神奇的数据结构,感觉不错。有学了个好东西,也不难。他主要应该是针对于数据统计的,例如本题的第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 }