1 10 10 0 5 2 7 5 4 3 8 7 7 2 8 6 3 5 0 1 3 1 1 9 4 0 1 0 3 5 5 5 5 1 4 6 3 1 5 7 5 7 3
Case 1: 4 0 0 3 1 2 0 1 5 1
题意:
输入cas
n m
输入n个数
输入m组 每组表示一个范围内l r 中小于num的数的个数
注意从l是0开始的
思路 :区间内找数 很容易联想到划分树 划分树是用来求一个区间内的第k大的数
那么我们可以找出第1第2第3.。。。。。。大数的值 和num进行比较
用二分法很容易找到第几大的数小于且等于num
下面的几乎就是个模板 二分法居然也搞了我一下 坑爹
#include<stdio.h> #include<algorithm> using namespace std; #define M 100005 int tree[40][M],sorted[M]; int toLeft[40][M]; void build(int level,int left,int right){ if(left==right)return ; int mid=(left+right)>>1; int i; int suppose;//假设在中位数sorted[mid]左边的数都全部小于sorted[mid] suppose=mid-left+1; for(i=left;i<=right;i++){ if(tree[level][i]<sorted[mid]){ suppose--; } } //如果suppose==1,则说明数组中值为sorted[mid]只有一个数。比如序列:1 3 4 5 6,sorted[mid]=4 /*如果suppose>1,则说明数组中左半边值为sorted[mid]的不止一个数,为mid-suppose。比如序列:1 4 4 4 6,sorted[mid]=4 */ int lpos=left,rpos=mid+1; for(i=left;i<=right;i++){ if(i==left) {//这里是预处理,相当与初始化 toLeft[level][i]=0; } else { toLeft[level][i]=toLeft[level][i-1]; } if(tree[level][i]<sorted[mid]){//划分到中位数左边 toLeft[level][i]++; tree[level+1][lpos++]=tree[level][i]; }else if(tree[level][i]>sorted[mid]){//划分到中位数右边 tree[level+1][rpos++]=tree[level][i]; }else{//这里,suppose大于0的数划分到中位数的左边 if(suppose!=0){//这里的处理太巧妙了!帅气! suppose--; toLeft[level][i]++; tree[level+1][lpos++]=tree[level][i]; }else{//表示 tree[level+1][rpos++]=tree[level][i]; } } } build(level+1,left,mid); build(level+1,mid+1,right); } //在[left,right]数据中查询[qleft,qright]中第k大的数据 int query(int level,int left,int right,int qleft,int qright,int k){ if( qleft==qright) return tree[level][qleft]; int s;//代表[left,qleft)之间有多个个元素被分到左边 int ss;//[qleft, qright]内将被划分到左子树的元素数目 int mid=(left+right)>>1; if(left==qleft){ s=0; ss=toLeft[level][qright]; }else{ s=toLeft[level][qleft-1]; ss=toLeft[level][qright]-s; } int newl,newr; if(k<=ss){//查询左边 newl=left+s; newr=left+s+ss-1; return query(level+1,left,mid,newl,newr,k); }else{//查询右边 newl=mid-left+1+qleft-s; newr=mid-left+1+qright-s-ss; return query(level+1,mid+1,right,newl, newr,k - ss); } } int main() { int n,m,cas,ca=0; scanf("%d",&cas); while(cas--) { printf("Case %d:\n",++ca); scanf("%d %d",&n,&m); int i; for(i=1;i<=n;i++){ scanf("%d",&tree[0][i]); sorted[i]=tree[0][i]; } sort(sorted+1,sorted+n+1); build(0,1,n); int ql,qr,num,ans=0; for(i=0;i<m;i++) { scanf("%d %d %d",&ql,&qr,&num); ql++;qr++;//注意这里哦 题意是按从0开始的 int mid,left=1,right=qr-ql+1,ans=0; mid=(left+right)/2; while(left<=right)//别少了等于号 { mid=(left+right)/2; if(query(0,1,n,ql,qr,mid)>num) right=mid-1; else {ans=mid;left=mid+1;}//我一开始写的二分法居然不知道是哪里出错了一个劲RE } printf("%d\n",ans); } } return 0; }