POJ 2104 K-th Number

这个题以前就做过了,用归并树+二分,2000+ms水过了。今天整理资料,zhk提到了划分树,于是去试了一下,经过几次小小的错误,最终过掉了,900+ms。时间效率很高,O(mlogn),是区间查询第k大值的利器。

该总结的东西基本上都写在注释里面了,就不再废话了。

 

ExpandedBlockStart.gif POJ 2104 K-th Number(划分树)
 1  // 从源序列开始,首先用buffer将此序列排序。
 2  // 每次划分的时候按照中点值将元素按照大小分别排在左子树和右子树,相当于一个排序的过程,用数组val记录。
 3  // 在划分的时候用数组toLeft记录每次划分时被分到左子树的元素的数目。
 4 
 5  #include  < stdio.h >
 6  #include  < string .h >
 7  #include  < algorithm >
 8 
 9  const   int  MaxN  =   100010 ;
10  int  val[ 20 ][MaxN], toLeft[ 20 ][MaxN];
11  int  srt[MaxN];
12  int  M, N;
13 
14  struct  Node {
15       int  l, r;
16  }node[MaxN << 3 ];
17 
18  inline  int  L( int  x) { return  (x << 1 ) + 1 ;}
19  inline  int  R( int  x) { return  (x << 1 ) + 2 ;}
20 
21  void  build_tree( int  s,  int  e,  int  cur_row,  int  id) {
22      node[id].l  =  s; node[id].r  =  e;
23       if (s  ==  e)
24           return ;
25       int  mid  =  (s + e) >> 1 ;
26       int  emN  =  mid  +   1   -  s;
27       for ( int  i  =  s; i  <=  e;  ++ i) {         // 记录与中位数相同的元素被分到左子树的数目。
28           if (val[cur_row][i]  <  srt[mid])     // 因为这些元素有可能被分到左子树,也有可能被分到右子树。
29              emN -- ;                         // 记录这个值便于后面的操作。
30      }
31       int  lp  =  s, rp  =  mid + 1 ;
32       for ( int  i  =  s; i  <=  e;  ++ i) {         // 计算被划分到左子树的元素个数。
33          toLeft[cur_row][i]  =  i != s ? toLeft[cur_row][i - 1 ]: 0 ;
34           if (val[cur_row][i]  <  srt[mid]) {
35              val[cur_row + 1 ][lp ++ =  val[cur_row][i];
36              toLeft[cur_row][i] ++ ;
37          }
38           else   if (val[cur_row][i]  >  srt[mid])
39              val[cur_row + 1 ][rp ++ =  val[cur_row][i];
40           else   if (emN) {                     // 当emN计数为0时,划分到左子树的中位数元素划分完毕。
41              val[cur_row + 1 ][lp ++ =  val[cur_row][i];
42              toLeft[cur_row][i] ++ ;
43              emN -- ;
44          }
45           else
46              val[cur_row + 1 ][rp ++ =  val[cur_row][i];
47      }
48      build_tree(s, mid, cur_row  +   1 , L(id));             // 递归建树。
49      build_tree(mid + 1 , e, cur_row  +   1 , R(id));
50  }
51 
52  // {[left part][query interval][right part]}        此为树的一个节点
53  // {[(LL)(RL)][(LI)(RI)][right part]}        
54  //         LL:划分到左子树的左边部分;    RL:划分到右子树的左边部分
55  //         LI:划分到左子树的中间部分;    RL:划分到右子树的中间部分
56  // 根据以上信息判断在哪个子树进行查询,并且重新计算query interval, 递归查询即可。
57 
58  int  query( int  s,  int  e,  int  k,  int  cur_row,  int  id) {
59       if (s  ==  e)                    
60           return  val[cur_row][s];
61       int  LL  =  s == node[id].l ? 0 :toLeft[cur_row][s - 1 ];     // 计算LL, LI
62       int  LI  =  toLeft[cur_row][e]  -  LL;
63       if (k  <=  LI)
64           return  query(node[id].l + LL, node[id].l + LL + LI - 1 , k, cur_row + 1 , L(id));
65       int  mid  =  (node[id].l  +  node[id].r)  >>   1 ;
66       int  RL  =  s  -  node[id].l  -  LL;                     // 计算RL, RI
67       int  RI  =  e  +   1   -  s  -  LI;
68       return  query(mid + 1 + RL, mid + 1 + RL + RI - 1 , k  -  LI, cur_row + 1 , R(id)); 
69  }
70 
71  int  main() {
72       while (scanf( " %d%d " & N,  & M)  ==   2 ) {
73           for ( int  i  =   0 ; i  <  N;  ++ i) {
74              scanf( " %d " , srt + i);
75              val[ 0 ][i]  =  srt[i];
76          }
77          std::sort(srt, srt  +  N);
78          build_tree( 0 , N - 1 0 0 );
79           int  s, e, k;
80           for ( int  i  =   0 ; i  <  M;  ++ i) {
81              scanf( " %d%d%d " & s,  & e,  & k);
82              printf( " %d\n " , query(s - 1 , e - 1 , k,  0 0 ));
83          }
84      }
85       return   0 ;
86  }
87 

 

 

ExpandedBlockStart.gif POJ 2104 K-th Number(归并+二分)
 1  #include  < stdio.h >
 2  #include  < string .h >
 3  #include  < algorithm >
 4 
 5  using   namespace  std;
 6 
 7  inline  int  MID( int  s,  int  e) { return  (s + e) >> 1 ;}
 8 
 9  const   int  MaxN  =   100010 ;
10  int  merge_tree[ 20 ][MaxN], n, m, dep[MaxN];
11  int  u, v, k, flag;
12 
13  void  Merge( int  begin,  int  end,  int  d) {
14       if (begin  +   1   ==  end) {
15          merge_tree[d][begin]  =  merge_tree[ 0 ][begin];
16          dep[begin]  =  d; 
17           return ;
18      }
19       int  Mid  =  MID(begin, end);
20      Merge(begin, Mid, d  +   1 );
21      Merge(Mid, end, d  +   1 );
22       int  i, j, k;
23       for (i  =  begin, j  =  Mid, k  =  begin; i  !=  Mid  &&  j  !=  end;) {
24           if (merge_tree[d + 1 ][i]  <  merge_tree[d + 1 ][j])
25              merge_tree[d][k ++ =  merge_tree[d + 1 ][i ++ ];
26           else
27              merge_tree[d][k ++ =  merge_tree[d + 1 ][j ++ ];
28      }
29       if (i  !=  Mid) {
30           for (; i  !=  Mid;  ++ i) merge_tree[d][k ++ =  merge_tree[d + 1 ][i];
31      }
32       if (j  !=  end) {
33           for (; j  !=  end;  ++ j) merge_tree[d][k ++ =  merge_tree[d + 1 ][j];
34      }
35  }
36 
37  int  search( int  begin,  int  end,  int  s,  int  e,  int  x,  int  d) {
38       int  Mid;
39       if (s  ==  begin  &&  end  ==  e) {
40           if (x  >  merge_tree[d][end - 1 ])
41               return  end  -  begin;
42           if (x  <  merge_tree[d][begin])
43               return   0 ;
44           int  u  =  begin  -   1 , v  =  end;
45           while (u + 1   <  v) {
46              Mid  =  MID(u, v);
47               if (merge_tree[d][Mid]  <=  x) {
48                   if (merge_tree[d][Mid]  ==  x)
49                      flag ++ ;
50                  u  =  Mid;
51              }
52               else
53                  v  =  Mid;
54          }
55           return  u  -  begin  +   1 ;
56      }
57      Mid  =  MID(begin, end);
58       if (s  >=  Mid)
59           return  search(Mid, end, s, e, x, d + 1 );
60       if (e  <=  Mid)
61           return  search(begin, Mid, s, e, x, d + 1 );
62       return  search(begin, Mid, s, Mid, x, d + 1 +  search(Mid, end, Mid, e, x, d + 1 );
63  }
64 
65  void  work() {
66       int  begin  =   - 1 , end  =  n, Mid;
67       while (begin  +   1   <  end) {
68          Mid  =  MID(begin, end);
69          flag  =   0 ;
70           int  sum  =  search( 0 , n, u, v + 1 , merge_tree[ 0 ][Mid],  0 );
71           if (flag  &&  sum  -  flag  <  k  &&  sum  >=  k) {
72              printf( " %d\n " , merge_tree[ 0 ][Mid]);
73               return ;
74          }
75           else   if (sum  <  k)
76              begin  =  Mid;
77           else
78              end  =  Mid;
79      }
80  }
81 
82  int  main() {
83       while (scanf( " %d%d " & n,  & m)  !=  EOF) {
84           for ( int  i  =   0 ; i  <  n;  ++ i) scanf( " %d " , merge_tree[ 0 +  i);
85          Merge( 0 , n,  0 );
86           for ( int  i  =   0 ; i  <  m;  ++ i) {
87              scanf( " %d%d%d " & u,  & v,  & k);
88              u -- ; v -- ;
89              work();
90          }
91      }
92       return   0 ;
93  }
94 

 

 

转载于:https://www.cnblogs.com/destinydesigner/archive/2010/12/05/1896959.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值