整体二分
相对于普通二分,整体二分用于处理较多询问.如果用普通二分去处理询问,效率将是O(Q*log×f(?))多出来的Q将无法让人承受。因而有了整体二分。整体二分与二分的最大区别就是对所有询问一同处理。所谓的一同处理,是指省去了二分中的冗余部分,上一个询问二分过这个区间,而当前询问又二分了一次,整体二分保证了没有这个冗余。
具体而言,当前带着所有询问传到了一个区间,整理得到一部分询问满足左区间,另一部分满足右区间(废话。。),那么把它们推入两个队列,搞进左右区间继续二分即可。这样就剪掉了时间效率中的Q,使得大量重复的二分被合并。
至于带修改的,明天填坑。
举个很著名的例子:查找区间K大,当然可以用主席树,但有的题并不行。
比如:矩阵乘法.(板子在此OwO)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#define N 505
using namespace std;
int n,m,tot,T,zhan[N*600],mark[N*600],ans[N*600],id[N*600],t[N][N];
struct node{int x,y,h;}a[N*N];
struct QQQ{int l1,l2,r1,r2,k;}q[N*600];
void add(int x,int y,int k)
{
for(int i=x;i<=n;i+=i&-i)
for(int j=y;j<=n;j+=j&-j)
t[i][j]+=k;
}
int q1(int x,int y)
{
int s=0;
for(int i=x;i>0;i-=i&-i)
for(int j=y;j>0;j-=j&-j)
s+=t[i][j];
return s;
}
int Q(int k)
{
int l1=q[k].l1,l2=q[k].l2,r1=q[k].r1,r2=q[k].r2;
return q1(l2,r2)+q1(l1-1,r1-1)-q1(l1-1,r2)-q1(l2,r1-1);
}//套二维树状数组
inline bool cmp(node a,node b){return a.h<b.h;}
void work(int l,int r,int L,int R)
{
if(L==R||l>r)return;
int mid=L+R>>1;
while(a[T+1].h<=mid&&T<tot){add(a[T+1].x,a[T+1].y,1);T++;}
while(a[T].h>mid){add(a[T].x,a[T].y,-1);T--;}
int sum=0;
for(int i=l;i<=r;i++)
{
if(Q(id[i])>q[id[i]].k-1)
{
mark[i]=1;ans[id[i]]=mid;sum++;
}
else mark[i]=0;
}
int l1=l,l2=l+sum;
for(int i=l;i<=r;i++)
if(mark[i])zhan[l1++]=id[i];
else zhan[l2++]=id[i];
for(int i=l;i<=r;i++)id[i]=zhan[i];
work(l,l1-1,L,mid);work(l1,l2-1,mid+1,R);
}
int main()
{
scanf("%d%d",&n,&m);int mm=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
scanf("%d",&a[++tot].h);
a[tot].x=i;a[tot].y=j;
mm=max(mm,a[tot].h);
}
sort(a+1,a+tot+1,cmp);
for(int i=1;i<=m;i++)scanf("%d%d%d%d%d",&q[i].l1,&q[i].r1,&q[i].l2,&q[i].r2,&q[i].k);
for(int i=1;i<=m;i++)id[i]=i;
work(1,m,0,mm+1);
for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}