Hdu-5412 CRB and Queries(整体二分)

185 篇文章 0 订阅
116 篇文章 0 订阅

Description

There are  N  boys in CodeLand. 
Boy  i  has his coding skill  Ai
CRB wants to know who has the suitable coding skill. 
So you should treat the following two types of queries. 
Query 1: 1  l v  
The coding skill of Boy  l  has changed to  v
Query 2: 2 l  r   k  
This is a report query which asks the  k -th smallest value of coding skill between Boy  l  and Boy  r (both inclusive). 

Input

There are multiple test cases. 
The first line contains a single integer  N
Next line contains  N  space separated integers  A1 A2 , …,  AN , where  Ai  denotes initial coding skill of Boy  i
Next line contains a single integer  Q  representing the number of queries. 
Next  Q  lines contain queries which can be any of the two types. 
1 ≤  N Q  ≤  105  
1 ≤  Ai v  ≤  109  
1 ≤  l  ≤  r  ≤  N  
1 ≤  k  ≤  r C l  + 1 

Output

For each query of type 2, output a single integer corresponding to the answer in a single line.

Sample Input

5
1 2 3 4 5
3
2 2 4 2
1 3 6
2 2 4 2

Sample Output

3
4


题意:带修改的区间第k大。


分析:整体二分的做法,对于每一个单独的询问,我们可以很容易设计一个nlogMAXAN的二分答案的算法,但是我们发现如果我们对每一个询问都这样处理的话会有很多重复的操作,于是我们考虑把所有询问一起二分,divide(l,r,x,y)表示l到r的询问所在的答案区间为[x,y],然后我们每次根据mid = (x + y)/2把所有的操作分成两份,左边是插入/删除值小于mid的操作和询问,右边是大于mid的操作和询问,然后递归下去,总复杂度还是n*logMAXANS。


</pre><pre name="code" class="cpp">#include<iostream>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<cstring>
#include<stack>
#include<cmath>
#include<queue>
#define INF 0x3f3f3f3f
#define MAXN 1000000000
#define N 100005
using namespace std;
int n,num,tot,q,a[N],f[N],ans[N],temp[N*3],MAX;
struct Ask
{
    int l,r,v,k,op,p,num;
}deal[N*3];
int lowbit(int x)
{
    return x & (-x);
}
void Insert(int k,int x)
{
    while(k <= n)
    {
        f[k] += x;
        k += lowbit(k);
    }
}
int Find(int k)
{
    int cnt = 0;
    while(k)
    {
        cnt += f[k];
        k -= lowbit(k);
    }
    return cnt;
}
void divide(int l,int r,int x,int y)
{
    if(l > r) return;
    if(x == y)
    {
        for(int i = l;i <= r;i++)
         if(deal[i].op == 3) ans[deal[i].num] = x;
        return;
    }
    int mid = (x+y)>>1;
    for(int i = l;i <= r;i++)
    {
        if(deal[i].op == 1 && deal[i].v <= mid) Insert(deal[i].p,1);              //修改
        if(deal[i].op == 2 && deal[i].v <= mid) Insert(deal[i].p,-1);
        if(deal[i].op == 3) temp[i] = Find(deal[i].r) - Find(deal[i].l-1);
    }
    for(int i = l;i <= r;i++)
    {
        if(deal[i].op == 1 && deal[i].v <= mid) Insert(deal[i].p,-1);             //还原
        if(deal[i].op == 2 && deal[i].v <= mid) Insert(deal[i].p,1);
    }
    vector<Ask> t1,t2;
    for(int i = l;i <= r;i++)
    {
        if(deal[i].op == 3)
        {
            if(temp[i] >= deal[i].k) t1.push_back(deal[i]);
            else
            {
                deal[i].k -= temp[i];
                t2.push_back(deal[i]);
            }
        }
        else
        {
            if(deal[i].v <= mid) t1.push_back(deal[i]);
            else t2.push_back(deal[i]);
        }
    }
    int l1 = t1.size(),l2 = t2.size();
    for(int i = 0;i < l1;i++) deal[i+l] = t1[i];
    for(int i = 0;i < l2;i++) deal[i+l1+l] = t2[i];
    divide(l,l+l1-1,x,mid);
    divide(l1+l,r,mid+1,y);
}
int main()
{
    while(~scanf("%d",&n))
    {
        MAX = num = tot = 0;
        for(int i = 1;i <= n;i++)
        {
            scanf("%d",&a[i]);
            deal[++num].op = 1,deal[num].p = i,deal[num].v = a[i];
            MAX = max(MAX,a[i]);
        }
        scanf("%d",&q);
        for(int i = 1;i <= q;i++)
        {
            int op;
            scanf("%d",&op);
            if(op == 1)
            {
                int l,v;
                scanf("%d%d",&l,&v);
                deal[++num].op = 2,deal[num].p = l,deal[num].v = a[l];
                deal[++num].op = 1,deal[num].p = l,deal[num].v = v;
                a[l] = v;
                MAX = max(MAX,v);
            }
            else
            {
                int l,r,k;
                scanf("%d%d%d",&l,&r,&k);
                deal[++num].op = 3,deal[num].l = l,deal[num].r = r,deal[num].k = k;
                deal[num].num = ++tot;
            }
        }
        divide(1,num,1,MAX);
        for(int i = 1;i <= tot;i++) printf("%d\n",ans[i]);
    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值