整体二分初识--POJ2104:K-th Number
n<=100000个数有m<=5000个询问,每次问区间第k大。
方法一:主席树!……
方法二:整体二分。
整体二分一次性计算半个值域对一个区间的询问的贡献,然后根据“这半边的贡献在某个询问中可不可以直接处理掉”把询问分两部分,并按“数字的值是否在这半边”把数字也分成两部分,这样把一个区间和值域都分掉了,然后就可以在f(n)logMax的时间出解,其中f(n)表示计算一次这样的贡献需要的时间。
在这题里,只需要看某个区间里在值域[L,mid]中出现的数字的个数有没有到K个,以此划分成两个区域。因此树状数组搞一搞,f(n)=nlogn,大功告成。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //#include<iostream> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cstdio> 5 //#include<bitset> 6 #include<algorithm> 7 //#include<cmath> 8 using namespace std; 9 10 int n,m; 11 #define maxn 200011 12 const int inf=0x3f3f3f3f; 13 struct App 14 { 15 int x,y,z,id,type; 16 //type=1 表示修改,其中x为位置,y为数值,z为在bit中的修改权值 17 //type=0 表示询问,其中x,y为左右端点,z是第几大,id询问编号 18 }a[maxn],al[maxn],ar[maxn]; 19 int ans[maxn]; 20 21 struct BIT 22 { 23 int a[maxn]; 24 void add(int x,int v) {for (;x<=n;x+=x&-x) a[x]+=v;} 25 int query(int x) {int ans=0; for (;x;x-=x&-x) ans+=a[x]; return ans;} 26 }t; 27 28 void solve(int L,int R,int ql,int qr) 29 { 30 if (ql>qr || L>R) return; 31 if (L==R) 32 { 33 for (int i=ql;i<=qr;i++) ans[a[i].id]=L; 34 return; 35 } 36 const int mid=(L+R)>>1; 37 int lal=0,lar=0; 38 for (int i=ql;i<=qr;i++) 39 { 40 if (a[i].type) 41 { 42 if (a[i].y<=mid) 43 { 44 t.add(a[i].x,a[i].z); 45 al[++lal]=a[i]; 46 } 47 else ar[++lar]=a[i]; 48 } 49 else 50 { 51 int tmp=t.query(a[i].y)-t.query(a[i].x-1); 52 if (tmp>=a[i].z) al[++lal]=a[i]; 53 else 54 { 55 ar[++lar]=a[i]; 56 ar[lar].z-=tmp; 57 } 58 } 59 } 60 for (int i=1;i<=lal;i++) if (al[i].type==1) t.add(al[i].x,-al[i].z); 61 for (int i=1,j=ql;i<=lal;i++,j++) a[j]=al[i]; 62 for (int i=1,j=ql+lal;i<=lar;i++,j++) a[j]=ar[i]; 63 solve(L,mid,ql,ql+lal-1); 64 solve(mid+1,R,ql+lal,qr); 65 } 66 67 int main() 68 { 69 scanf("%d%d",&n,&m); 70 for (int i=1;i<=n;i++) scanf("%d",&a[i].y),a[i].x=i,a[i].z=1,a[i].type=1; 71 for (int i=1,j=n+1;i<=m;i++,j++) scanf("%d%d%d",&a[j].x,&a[j].y,&a[j].z),a[j].id=i,a[j].type=0; 72 solve(-inf,inf,1,n+m); 73 for (int i=1;i<=m;i++) printf("%d\n",ans[i]); 74 return 0; 75 }