hdu 4117 Super Mario
题目描述
给定一个序列,询问某段区间内小于等于k的数的个数。
分析
划分树+二分,二分的同时统计满足题意的数的个数。。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define N 100100 5 #define lson l,m,n<<1 6 #define rson m+1,r,n<<1|1 7 using namespace std; 8 int sorted[N],ans[N][20],num[N][20]; 9 void build(int l,int r,int n,int d){ 10 if(l==r)return; 11 int m=(l+r)>>1,lsame=m-l+1; 12 for(int i=l;i<=r;i++) 13 if(ans[i][d]<sorted[m]) lsame--; 14 int lpos=l,rpos=m+1; 15 for(int i=l;i<=r;i++){ 16 if(i==l) num[i][d]=0; 17 else num[i][d]=num[i-1][d]; 18 if(ans[i][d]<sorted[m]){ 19 num[i][d]++; 20 ans[lpos++][d+1]=ans[i][d]; 21 } 22 else if(ans[i][d]>sorted[m]) 23 ans[rpos++][d+1]=ans[i][d]; 24 else{ 25 if(lsame>0){ 26 lsame--; 27 num[i][d]++; 28 ans[lpos++][d+1]=ans[i][d]; 29 } 30 else 31 ans[rpos++][d+1]=ans[i][d]; 32 } 33 } 34 build(lson,d+1); 35 build(rson,d+1); 36 } 37 //求区间第k小的数 38 int query(int ll,int rr,int k,int l,int r,int n,int d){ 39 if(l==r)return ans[l][d]; 40 int s,ss; 41 int m=(l+r)>>1; 42 if(ll==l) s=0; 43 else s=num[ll-1][d]; 44 ss=num[rr][d]-s; 45 if(ss>=k) 46 return query(l+s,l+s+ss-1,k,lson,d+1); 47 else{ 48 int xx,x; 49 xx=ll-l-s; 50 x=rr-ll+1-ss; 51 return query(m+xx+1,m+x+xx,k-ss,rson,d+1); 52 } 53 } 54 int n,m,t; 55 int BS(int l,int r,int key){ 56 int ll=1,rr=r-l+1; 57 int ans=0; 58 while(ll<=rr){ 59 int m=(ll+rr)>>1; 60 int x=query(l,r,m,1,n,1,1); 61 if(key>=x){ 62 ans+=(m-ll+1); 63 ll=m+1; 64 } 65 else if(key<x) rr=m-1; 66 } 67 return ans; 68 } 69 int main(){ 70 int d=1; 71 scanf("%d",&t); 72 while(t--){ 73 scanf("%d%d",&n,&m); 74 for(int i=1;i<=n;i++){ 75 scanf("%d",&sorted[i]); 76 ans[i][1]=sorted[i]; 77 } 78 sort(sorted+1,sorted+1+n); 79 build(1,n,1,1); 80 printf("Case %d:\n",d++); 81 while(m--){ 82 int l,r,k; 83 scanf("%d%d%d",&l,&r,&k);l++;r++; 84 printf("%d\n",BS(l,r,k)); 85 } 86 } 87 return 0; 88 }