hdu 5412 CRB and Queries 2015多校联合训练赛#10 分治 求区间第k大数

CRB and Queries

Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 636    Accepted Submission(s): 162


Problem 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  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
 

Author
KUT(DPRK)
 

Source
2015 Multi-University Training Contest 10



求区间第k大数,http://www.cnblogs.com/zig-zag/archive/2013/04/18/3027707.html 参考这篇博客,然后看2013年集训队XHR论文。吧

解法:操作分三种,

1. 加入一个数,

2. 删除一个数,

3. 询问答案在左区间还是又区间

方法:

二分询问的答案是什么。

对于初始数字,变为插入操作

按操作的时间顺序排列各个操作,对于修改操作拆为删除和加入操作

:1 删除之前插入的数字,2. 加入新的数字

接下来分治二分答案:

对于mid,如果插入或者删除的数字<=mid那么应该放到左区间,并且用树状数组(其他数据结构也行)

维护前X个位置有多少个数字在左边。

对于询问:如果l,r区间在左边的数字>=k那么答案在左边,否则答案在右边,并且更新K,k=k-这个区间去左边的数字个数

当low =high的时候,说明low就是答案了,只要更新询问还在low,low之间的答案即可。

复杂度分析:分治的深度是log(S)s是数据的范围。

每个询问每次被分到左边或者右边,会有log(s)次操作。每个插入或者删除也是。

但是要用树状数组维护多少个数字在左边,要log(n)次更新或者查询。

复杂度是n*log(s)*log(n)





#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
using namespace std;
#define maxn 300007
int tree[maxn];
void add(int p,int n){
    for(;p<maxn;p+=p&(-p))
        tree[p]+=n;
}
int query(int p){
    int ans = 0;
    for(;p>0;p-=p&(-p))
        ans += tree[p];
    return ans;
}

struct Node{
    int l,r,k,ty,ans;
};
Node p[maxn];
int id1[maxn],id2[maxn];
void CDQ(int L,int R,int low,int high){
    if(R < L) return ;
    if(low == high ){
        for(;L<=R;L++){
            p[id1[L]].ans = low;
        }
        return ;
    }
    int mid = (low+high)/2,l=L,r=R,k,u;
    for(int i = L;i <= R; i++){
        u = id1[i];
       if(p[u].ty == 2){
            k = query(p[u].r) - query(p[u].l-1);
            if(k >= p[u].k) id2[l++] = u;
            else {
                p[u].k -= k;
                id2[r--] = u;
            }
        }
        else if(p[u].k <= mid){
            add(p[u].l,p[u].ty);
            id2[l++] = u;
        }
        else id2[r--] =  u;
    }

    for(int i = L; i <= R; i++){
        u = id1[i];
        if(p[u].ty != 2 && p[u].k <= mid) add(p[u].l,-p[u].ty);
    }
    for(k=L;k<l;k++)
        id1[k] = id2[k];
    for(r=R;k<=R;k++)
        id1[k] = id2[r--];
    CDQ(L,l-1,low,mid);
    CDQ(l,R,mid+1,high);
}

int num[maxn];

int main(){
    int n,q,t,cnt;
    memset(tree,0,sizeof(tree));
    while(scanf("%d",&n)!=EOF){
        for(cnt=0;cnt<n;cnt++){
            scanf("%d",&p[cnt].k);
            p[cnt].ty = 1;
            p[cnt].l = cnt+1;
            num[cnt+1] = p[cnt].k;
        }
        scanf("%d",&q);
        int ty,l,v;
        for(int i = 0;i < q; i++,cnt++){
            scanf("%d",&p[cnt].ty);
            if(p[cnt].ty == 1){
                scanf("%d%d",&l,&v);
                p[cnt].ty = -1;
                p[cnt].k = num[l];
                p[cnt].l = l;
                cnt++;
                num[l] = v;
                p[cnt].ty = 1;
                p[cnt].k = v;
                p[cnt].l = l;
            }
            else {
                scanf("%d%d%d",&p[cnt].l,&p[cnt].r,&p[cnt].k);
            }
        }
        for(int i = 0;i < cnt; i++)
            id1[i] = i;
        CDQ(0,cnt-1,0,1000000000);
        for(int i = 0;i < cnt; i++){
            if(p[i].ty == 2) printf("%d\n",p[i].ans);
        }
    }
    return 0;
}


你可能感兴趣的:(分治,HDU,and,Queries,CRB,5412,2015多校联合训练赛#10,求区间第k大数)