hud4417--Super Mario(划分树+二分)

求区间比K小或等于K的元素个数

View Code
  1 //Accepted    4417    593MS    16388K    3445 B    G++    asen11
  2 #include <stdio.h>
  3 #include <algorithm>
  4 using namespace std;
  5 #define M 100001
  6 #define LL(x)     ((x)<<1)
  7 #define RR(x)     ((x)<<1|1)
  8 struct Seg_Tree{
  9     int left,right;
 10     int mid() {
 11         return (left + right) >> 1;
 12     }
 13 }tt[M*4];
 14 int len;
 15 int sorted[M];
 16 int toLeft[20][M];
 17 int val[20][M];
 18  
 19 void build(int l,int r,int d,int idx) {
 20     tt[idx].left = l;
 21     tt[idx].right = r;
 22     if(tt[idx].left == tt[idx].right)    return ;
 23     int mid = tt[idx].mid();
 24     int lsame = mid - l + 1;//lsame表示和val_mid相等且分到左边的
 25     for(int i = l ; i <= r ; i ++) {
 26         if(val[d][i] < sorted[mid]) {
 27             lsame --;//先假设左边的数(mid - l + 1)个都等于val_mid,然后把实际上小于val_mid的减去
 28         }
 29     }
 30     int lpos = l;
 31     int rpos = mid+1;
 32     int same = 0;
 33     for(int i = l ; i <= r ; i ++) {
 34         if(i == l) {
 35             toLeft[d][i] = 0;//toLeft[i]表示[ tt[idx].left , i ]区域里有多少个数分到左边
 36         } else {
 37             toLeft[d][i] = toLeft[d][i-1];
 38         }
 39         if(val[d][i] < sorted[mid]) {
 40             toLeft[d][i] ++;
 41             val[d+1][lpos++] = val[d][i];
 42         } else if(val[d][i] > sorted[mid]) {
 43             val[d+1][rpos++] = val[d][i];
 44         } else {
 45             if(same < lsame) {//有lsame的数是分到左边的
 46                 same ++;
 47                 toLeft[d][i] ++;
 48                 val[d+1][lpos++] = val[d][i];
 49             } else {
 50                 val[d+1][rpos++] = val[d][i];
 51             }
 52         }
 53     }
 54     build(l,mid,d+1,LL(idx));
 55     build(mid+1,r,d+1,RR(idx));
 56 }
 57  
 58 int query(int l,int r,int k,int d,int idx) {
 59     if(l == r) {
 60         return val[d][l];
 61     }
 62     int s;//s表示[ l , r ]有多少个分到左边
 63     int ss;//ss表示 [tt[idx].left , l-1 ]有多少个分到左边
 64     if(l == tt[idx].left) {
 65         s = toLeft[d][r];
 66         ss = 0;
 67     } else {
 68         s = toLeft[d][r] - toLeft[d][l-1];
 69         ss = toLeft[d][l-1];
 70     }
 71     if(s >= k) {//有多于k个分到左边,显然去左儿子区间找第k个
 72         int newl = tt[idx].left + ss;
 73         int newr = tt[idx].left + ss + s - 1;//计算出新的映射区间
 74         return query(newl,newr,k,d+1,LL(idx));
 75     } else {
 76         int mid = tt[idx].mid();
 77         int bb = l - tt[idx].left - ss;//bb表示 [tt[idx].left , l-1 ]有多少个分到右边
 78         int b = r - l + 1 - s;//b表示 [l , r]有多少个分到右边
 79         int newl = mid + bb + 1;
 80         int newr = mid + bb + b;
 81         return query(newl,newr,k-s,d+1,RR(idx));
 82     }
 83 }
 84 
 85 void solve(int l,int r,int maxx,int L,int R)//二分查找区间第K小元素
 86 {
 87     if(l >= r)
 88     {         
 89         int s = query(L,R,r,0,1);
 90         if(s > maxx)
 91              printf("%d\n",r-1);
 92         else
 93             printf("%d\n",r);
 94         return ;
 95     }
 96     int mid = ( l + r ) / 2;
 97     int s = query(L,R,mid,0,1);
 98     if(s > maxx)
 99     {
100         solve(l,mid,maxx,L,R);
101     }
102     else
103     {
104         solve(mid+1,r,maxx,L,R);
105     }
106 }
107  
108 int main() {
109     int T,x=0;
110     scanf("%d",&T);
111     while(T --) {
112         x++;
113         int n , m;
114         scanf("%d%d",&n,&m);
115         for(int i = 1; i <= n; i++){
116             scanf("%d",&val[0][i]);
117             sorted[i] = val[0][i];
118         }
119         sort(sorted + 1 , sorted + n + 1);
120         build(1,n,0,1);
121         printf("Case %d:\n",x);
122         while(m --) {
123             int l,r,maxx;
124             scanf("%d%d%d",&l,&r,&maxx);
125             l++;
126             r++;
127             solve(1,r-l+1,maxx,l,r);
128         }
129     }
130     return 0;
131 }
posted @ 2012-09-23 19:54  Wheat″  阅读(330)  评论(0)    收藏  举报