BZOJ1901:Zju2112 Dynamic Rankings——题解

http://www.lydsy.com/JudgeOnline/problem.php?id=1901

Description

给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1

],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改

变后的a继续回答上面的问题。

Input

第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000)。

分别表示序列的长度和指令的个数。

第二行有n个数,表示a[1],a[2]……a[n],这些数都小于10^9。

接下来的m行描述每条指令

每行的格式是下面两种格式中的一种。

Q i j k 或者 C i t

Q i j k (i,j,k是数字,1≤i≤j≤n, 1≤k≤j-i+1)

表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数。

C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改变成为t

m,n≤10000

Output

对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。

Sample Input

5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

Sample Output

3
6

————————————————————————————

带修改主席树板子题目(bzoj真喜欢权限板子题)

然后我学了一上午:https://www.cnblogs.com/candy99/p/6166467.html

总的来说如果我们暴力修改主席树的话,我们对于每一棵主席树都需要进行修改,那么这样时间复杂度就爆棚了。

而我们考虑,树状数组和线段树的修改仅仅只是logn的。

所以我们试着让树状数组套上主席树,这样就能方便的修改值了。

也就是说,树状数组的每一个节点都挂着一棵主席树,这样所需要修改的主席树就变成O(logn)棵了。

那么查询也很简单,就是树状数组的查询方法(因为我们外层包的是树状数组,所以查询就是在查主席树的根,也就不需要主席树了)。

显然空间复杂度为O(nlognlogn)

!但是!我学的那篇博客提供了一种O(2nlogn)的做法。

我们直接将修改操作另开一棵树状数组(貌似树状数组节点还套了个线段树?我不是很懂啊)维护,这样就变成了主席树+树状数组了,查询的时候两者的和一加即可。

注意事项:

1.第二种做法主席树和树状数组的插入方法是不一样的,为了减少码量强行压成了一个函数,所以二者的意义是不一样的。

2.空间记得根据空间复杂度开。

3.不要用什么玄学的stl,不然AC变TLE就是一瞬间的事情。

4.离散化。

5.我们既然决定要将树状数组和主席树分来,那么根节点就不能公用(这也是复杂度多了个2的原因)。

#include<cstdio>
#include<queue>
#include<cctype>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
const int N=20010;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch==‘-‘;ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
struct tree{
    int l,r,sum;
}tr[N*100];
struct question{
    char s[10];
    int i,j,k,t;
}q[N];
int a[N],b[N],rt[N],root[N],n,m,Q,pool;
inline void initLSH(){
    sort(b+1,b+m+1);
    m=unique(b+1,b+m+1)-b-1;
    return;
}
inline int LSH(int v){return lower_bound(b+1,b+m+1,v)-b;}
inline int lowbit(int x){return x&-x;}
inline void insert(int &x,int l,int r,int p,int v){
    tr[++pool]=tr[x];x=pool;
    tr[x].sum+=v;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(p<=mid)insert(tr[x].l,l,mid,p,v);
    else insert(tr[x].r,mid+1,r,p,v);
    return;
}
void add(int pos,int v){
    int k=LSH(a[pos]);
    for(int i=pos;i<=n;i+=lowbit(i))insert(root[i],1,m,k,v);
    return;
}
int q1[N],t1,q2[N],t2;
inline int cal(){
    int sum1=0,sum2=0;
    for(int i=1;i<=t1;i++)sum1+=tr[tr[q1[i]].l].sum;
    for(int i=1;i<=t2;i++)sum2+=tr[tr[q2[i]].l].sum;
    return sum2-sum1;
}
inline int query(int nl,int nr,int k){
    int l=1,r=m;t1=t2=0;
    for(int i=nl;i;i-=lowbit(i))q1[++t1]=root[i];
    for(int i=nr;i;i-=lowbit(i))q2[++t2]=root[i];
    nl=rt[nl];nr=rt[nr];
    while(l<r){
    int ls=cal()+tr[tr[nr].l].sum-tr[tr[nl].l].sum,mid=(l+r)>>1;
    if(k<=ls){
        for(int i=1;i<=t1;i++)q1[i]=tr[q1[i]].l;
        for(int i=1;i<=t2;i++)q2[i]=tr[q2[i]].l;
        nl=tr[nl].l;nr=tr[nr].l;
        r=mid;
    }else{
        for(int i=1;i<=t1;i++)q1[i]=tr[q1[i]].r;
        for(int i=1;i<=t2;i++)q2[i]=tr[q2[i]].r;
            nl=tr[nl].r;nr=tr[nr].r;
            l=mid+1;k-=ls;
        }
    }
    return l;
}
int main(){
    n=read();
    Q=read();
    for(int i=1;i<=n;i++)a[i]=b[++m]=read();
    for(int i=1;i<=Q;i++){
    scanf("%s",q[i].s);
    if(q[i].s[0]==‘Q‘){
        q[i].i=read();q[i].j=read();q[i].k=read();
    }
    else{
        q[i].i=read();
        q[i].t=b[++m]=read();
    }
    }
    initLSH();
    for(int i=1;i<=n;i++)rt[i]=rt[i-1],insert(rt[i],1,m,LSH(a[i]),1);
    for(int i=1;i<=Q;i++){
    if(q[i].s[0]==‘Q‘)
        printf("%d\n",b[query(q[i].i-1,q[i].j,q[i].k)]);
    else{
        add(q[i].i,-1);
        a[q[i].i]=q[i].t;
        add(q[i].i,1);
    }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/luyouqi233/p/8157534.html

时间: 2024-10-10 11:08:08

BZOJ1901:Zju2112 Dynamic Rankings——题解的相关文章

【树状数组套权值线段树】bzoj1901 Zju2112 Dynamic Rankings

谁再管这玩意叫树状数组套主席树我跟谁急 明明就是树状数组的每个结点维护一棵动态开结点的权值线段树而已 好吧,其实只有一个指针,指向该结点的权值线段树的当前结点 每次查询之前,要让指针指向根结点 不同结点的权值线段树之间毫无关联 可以看这个:http://blog.csdn.net/popoqqq/article/details/40108669?utm_source=tuicool #include<cstdio> #include<algorithm> using namespa

BZOJ1901 Zju2112 Dynamic Rankings 【树状数组套主席树】

题目 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]--a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改 变后的a继续回答上面的问题. 输入格式 第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000). 分别表示序列的长度和指令的个数. 第二行有n个数,表示a[1],a[2]--a[n],这些数都小于10^9. 接下来的m

bzoj1901(Zju2112 Dynamic Rankings)

题目链接:没有权限,进不去,题目也没法交,代码也不知道对不对,有好心人有权限帮忙交下吧QAQ 题目大意:带区间修改的第K小数 题目思路:有树套树,块状链表等优秀数据结构可以解决该题,当然我还是继续练习整体二分. 把修改操作拆开成两个操作 1.删除节点上的数 2.加入一个新数,然后更新的时候注意删除操作对树状数组的更新与插入操作相反,其他的没有难度 #include <iostream> #include <cstdio> #include <cstdlib> #incl

【分块】bzoj1901 Zju2112 Dynamic Rankings

区间k大,分块大法好,每个区间内存储一个有序表. 二分答案,统计在区间内小于二分到的答案的值的个数,在每个整块内二分.零散的暴力即可. 还是说∵有二分操作,∴每个块的大小定为sqrt(n*log2(n))比较快呢. 1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<cmath> 5 using namespace std; 6 int n,a[10001],num[

BZOJ 1901 Zju2112 Dynamic Rankings 题解

题意:带修改不带插入的区间k大. 裸的可持久化线段树..由于有修改,要用树状数组维护.其它跟不带修改的可持久化线段树一样. 因为我没有找到网上用指针写的代码..CLJ写这道题也用的不是可持久化线段树,于是我就没有任何模板可以参照..就参考网上数组版的自己脑补了一个指针版..你们就可以看到代码优美度下降了好多. 由于数字范围很大,我们需要把所有输入读进来然后离散化..不离散化的话就要动态开点(这个我暂时还不会) 这份代码用的空间比较多..在zju是A不了的,需要空间优化(怎么优化我也不知道) 1

【分块】【权值分块】bzoj1901 Zju2112 Dynamic Rankings

论某O(n*sqrt(n))的带修改区间k大值算法. 首先对序列分块,分成sqrt(n)块. 然后对权值分块,共维护sqrt(n)个权值分块,对于权值分块T[i],存储了序列分块的前i块的权值情况. 对于区间询问,需要获得区间中每个值出现的次数,然后按权值扫O(sqrt(n)),完整的部分我们可以通过权值分块差分(O(1))得到(比如Lb~Rb块就是T[Rb]-T[Lb-1]),零散的部分我们再维护一个额外的权值分块,累计上该值即可.O(sqrt(n)). 对于修改,直接在该位置之后的所有权值分

1901: Zju2112 Dynamic Rankings

1901: Zju2112 Dynamic Rankings Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 5268  Solved: 2207[Submit][Status][Discuss] Description 给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i

【BZOJ-1901】Dynamic Rankings 带修主席树

1901: Zju2112 Dynamic Rankings Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 7292  Solved: 3038[Submit][Status][Discuss] Description 给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i

bzoj 1901: Zju2112 Dynamic Rankings(树套树)

1901: Zju2112 Dynamic Rankings 经典的带修改求区间第k小值问题 树套树模板,我是用的线段树套splay实现的,而且用的数组模拟的,所以可能空间略大,bzoj过了,zoj过不了. 思路很简单,用线段树维护区间,用splay维护区间内的权值,然后询问的时候,二分答案key,然后在区间内找小于key的数有多少个. 贴上模板: #include<stdio.h> #include<string.h> #include<algorithm> #def