HDU 4417 划分树+二分

来源:互联网 发布:排水沟标高计算软件 编辑:程序博客网 时间:2024/05/21 06:16



题意:有n个数,m个询问(l,r,k),问在区间[l,r] 有多少个数小于等于k。


划分树——查找区间第k大的数。。。。


利用划分树的性质,二分查找在区间[l,r]小于等于k的个数。

如果在区间第 i 大的数tmp>k,则往下找,如果tmp<k,往上找。



 

#include<cstdio>#include<stdlib.h>#include<string.h>#include<string>#include<map>#include<cmath>#include<iostream>#include <queue>#include <stack>#include<algorithm>#include<set>using namespace std;#define INF 1e8#define eps 1e-8#define LL long long #define N 100010#define mod  1000000007 int a[N],s[N],t[20][N],num[20][N],n,m;void build(int c,int l,int r){if(l==r) return;int mid=(l+r)/2;int lm=mid-l+1,lp=l,rp=mid+1;for(int i=l;i<=r;i++)lm-=s[i]<s[mid];for(int i=l;i<=r;i++){if(i==l)num[c][i]=0;else num[c][i]=num[c][i-1];if(t[c][i]==s[mid]){if(lm){lm--;num[c][i]++;t[c+1][lp++]=t[c][i];}else t[c+1][rp++]=t[c][i];}else if(t[c][i]<s[mid]){num[c][i]++;t[c+1][lp++]=t[c][i];}else t[c+1][rp++]=t[c][i];}build(c+1,l,mid);build(c+1,mid+1,r);}int query(int c,int l,int r,int ql,int qr,int k){if(l==r)return t[c][l];int s,ss,mid=(l+r)/2;if(ql==l)s=0,ss=num[c][qr];else s=num[c][ql-1],ss=num[c][qr]-num[c][ql-1];if(k<=ss)return query(c+1,l,mid,l+s,s+l+ss-1,k);else return query(c+1,mid+1,r,mid+1+ql-l-s,mid+1+qr-l-s-ss,k-ss);}int main(){int T,ca=1;scanf("%d",&T);while(T--){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%d",&a[i]);s[i]=t[0][i]=a[i];}sort(s+1,s+n+1);build(0,1,n);printf("Case %d:\n",ca++);while(m--){int l,r,k;scanf("%d%d%d",&l,&r,&k);l++;r++;if(query(0,1,n,l,r,r-l+1)<=k)printf("%d\n",r-l+1);else if(query(0,1,n,l,r,1)>k)puts("0");else {int ll=0,rr=r-l+1,mid;while(ll<=rr){mid=(ll+rr)>>1;int tmp=query(0,1,n,l,r,mid);//如果在区间[l,r]第mid大的数大于k,rr=mid-1if(tmp>k)rr=mid-1;else ll=mid+1;//否则ll=mid+1}printf("%d\n",ll-1);}}}return 0;}/*110 100 5 2 7 5 4 3 8 7 7 2 8 63 5 01 3 11 9 40 1 03 5 55 5 14 6 31 5 75 7 3*/

0 0