HDU 5412 CRB and Queries (Kth number 整体二分 动态转静态)

题意:

给出长度为n的数列,m次操作

操作分为 1 x y 将x位置的数改成y

2 x y k 查询【x , y】区间内的第k小数


分析:

有了POJ 2104 那题的静态整体二分的基础后就非常好做了

还是离线处理的方法,将所有数据读入然后再整体二分

对于修改操作,将它分为删除操作和添加操作,仔细想想,这样做并不影响整体二分的结果,因为一次修改操作只对后面的有影响,而后面的影响会随着二分消除掉。

要注意一点,由于修改操作改为了两种操作,所以总空间应该开到 n+2*m


#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

#define INF 0x3f3f3f3f
#define maxn 320000

struct node
{
    int val,l,r,k;
    int index,kind;
} q[maxn],q1[maxn],q2[maxn];

int n,m;
int a[maxn];
int c[maxn];
int ans[maxn];

inline int lowbit(int x)
{
    return x&-x;
}

inline void update(int x,int val)
{
    for(; x<=n; x+=lowbit(x)) c[x]+=val;
}

inline int query(int x)
{
    int sum=0;
    for(; x>0; x-=lowbit(x)) sum+=c[x];
    return sum;
}

void divide(int s,int t,int l,int r)
{
    if(s>t) return ;
    if(l==r)
    {
        for(int i=s; i<=t; i++)
        {
            if(q[i].kind==2)
            {
                ans[q[i].index]=l;
            }
        }
        return ;
    }
    int mid=(l+r)>>1;
    int num1=0,num2=0;
    int flag1=0,flag2=0;
    for(int i=s; i<=t; i++)
    {
        if(q[i].kind==2)
        {
            int tmp=query(q[i].r)-query(q[i].l-1);
            if(tmp>=q[i].k) q1[num1++]=q[i],flag1=1;
            else q[i].k-=tmp,q2[num2++]=q[i],flag2=1;
        }
        else
        {
            if(q[i].val<=mid) update(q[i].index,q[i].kind),q1[num1++]=q[i];
            else q2[num2++]=q[i];
        }
    }
    for(int i=s; i<=t; i++)
    {
        if(q[i].kind!=2)
        {
            if(q[i].val<=mid) update(q[i].index,-q[i].kind);
        }
    }
    for(int i=0; i<num1; i++) q[s+i]=q1[i];
    for(int i=0; i<num2; i++) q[s+num1+i]=q2[i];
    if(flag1) divide(s,s+num1-1,l,mid);
    if(flag2) divide(s+num1,t,mid+1,r);
}

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        memset(c,0,sizeof c);
        int cnt=1;
        int mm=0;
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            q[cnt].val=a[i];
            q[cnt].index=i;
            q[cnt].kind=1;
            cnt++;
        }
        scanf("%d",&m);
        int num=0;
        for(int i=1; i<=m; i++)
        {
            int op;
            scanf("%d",&op);
            if(op==2)
            {
                int l,r,k;
                scanf("%d%d%d",&l,&r,&k);
                q[cnt].l=l;
                q[cnt].r=r;
                q[cnt].k=k;
                q[cnt].kind=2;
                q[cnt].index=num++;
                cnt++;
            }
            else
            {
                int x,val;
                scanf("%d%d",&x,&val);
                q[cnt].val=a[x];
                q[cnt].index=x;
                q[cnt].kind=-1;
                cnt++;
                q[cnt].index=x;
                q[cnt].val=val;
                q[cnt].kind=1;
                cnt++;
                a[x]=val;
            }
        }
        divide(1,cnt-1,1,INF);
        for(int i=0; i<num; i++)
        {
            printf("%d\n",ans[i]);
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值