整体二分,初步学习

         参考博客由于国庆hdu升级进不去,尝试了ZOJ2112   空间消耗减少了20000多

          

         在学习区间第k大时,学了主席树,主席树空间消耗很大,有些题过不了,然后又开始学整体二分,记录下个人理解

        整体二分正如他的名字,把所有操作和查询放一起,二分查找答案,开始时,我也很懵逼,这怎么实现。。。。。。。。。

后面通过看别人代码,发现也不难理解,他的整体操作就是,先把所有的输入记录下来,就是离线,把修改操作分为连续的两步操作,一步是把第x位的修改前num的个数减一,把第x位的修改后num加1,类似树状数组套主席树的修改,而前n个输入就是一步直接加上num,写一个结构体,用个op标记,op=2,表示查询,op=1表示插入,op=-1,表示删除某个数,这样就把输入的n个数和q个操作合为了一个整体,再然后就是离散数据后,对数据进行二分查找答案,

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100010
#define lowbit(x) x&(-x)
struct Node
{
    int op,x,y,k,id;
}qu[MAXN*3],q1[MAXN<<1],q2[MAXN<<1];
int a[MAXN],num[MAXN<<1],sum[MAXN],n,ans[MAXN];
void update(int x,int v)
{
    while(x<=n)
    {
        sum[x]+=v;
        x+=lowbit(x);
    }
}
int query(int x)
{
    int res=0;
    while(x>0)
    {
        res+=sum[x];
        x-=lowbit(x);
    }
    return res;
}
void solve(int x,int y,int l,int r)//x到y操作 l到r答案
{
    if(l==r)
    {
        for(int i=x;i<=y;i++)
        {
            if(qu[i].op==2)ans[qu[i].id]=num[l];
        }
        return;
    }
    int mid=(l+r)/2,c1=0,c2=0;
    for(int i=x;i<=y;i++)
    {
        if(qu[i].op==2)
        {
            int d=query(qu[i].y)-query(qu[i].x-1);
            if(qu[i].k<=d)
            {
                q1[c1++]=qu[i];
            } else
            {
                qu[i].k-=d;
                q2[c2++]=qu[i];
            }
        } else
        {
            if(qu[i].y<=mid)
            {
                update(qu[i].x,qu[i].op);
                q1[c1++]=qu[i];
            } else q2[c2++]=qu[i];
        }
    }
    for(int i=0;i<c1;i++)
    {
        if(q1[i].op!=2)update(q1[i].x,-q1[i].op);
    }
    memcpy(qu+x,q1,sizeof(Node)*c1);
    memcpy(qu+x+c1,q2,sizeof(Node)*c2);
    solve(x,x+c1-1,l,mid);
    solve(x+c1,y,mid+1,r);
}
void write(int q)
{
    for(int i=0;i<q;i++)
    {
        printf("%d\n",ans[i]);
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--) {
        int q,x,y,k;
       // memset(sum,0,sizeof(sum));
        scanf("%d %d",&n,&q);
        int all = 1, tot = 0,query_id=0;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            num[tot++]=a[i];
            qu[all++]={1,i,a[i]};
        }
        scanf("%d",&q);
        char op[2];
        for(int i=0;i<q;i++)
        {
            scanf("%s %d %d",op,&x,&y);
            if(op[0]=='Q')
            {
                scanf(" %d",&k);
                qu[all++]={2,x,y,k,query_id++};
            } else
            {
                qu[all++]={-1,x,a[x]};
                qu[all++]={1,x,y};
                a[x]=y;
                num[tot++]=y;
            }
        }
        sort(num,num+tot);
        int cnt=unique(num,num+tot)-num;
        for(int i=1;i<all;i++)
        {
            if(qu[i].op!=2)
            {
                qu[i].y=lower_bound(num,num+cnt,qu[i].y)-num;
            }
        }
        solve(1,all-1,0,cnt-1);
        write(query_id);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值