整体二分

相对于普通二分,整体二分用于处理较多询问.如果用普通二分去处理询问,效率将是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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值