【树状数组套主席树】带修改区间K大数

P2617 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继续回答上面的问题。你需要编一个这样的程序,从输入文件中读入序列a,然后读入一系列的指令,包括询问指令和修改指令。

对于每一个询问指令,你必须输出正确的回答。

输入输出格式
输入格式:
第一行有两个正整数n(1≤n≤100000),m(1≤m≤100000)。分别表示序列的长度和指令的个数。

第二行有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。

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

输入输出样例
输入样例#1: 
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
输出样例#1: 
3
6
说明
10%的数据中,m,n≤100;

20%的数据中,m,n≤1000;

50%的数据中,m,n≤10000。

对于所有数据,m,n≤100000

请注意常数优化,但写法正常的整体二分和树套树都可以以大约1000ms每个点的时间过。

来源:bzoj1901

本题数据为洛谷自造数据,使用CYaRon耗时5分钟完成数据制作。

一些奇怪的东西

这个大概是luogu某题目,然而我太菜了,所以现在才会做。

这个题目显然是由一些静态的东西衍生而来的(没错就是静态区间K大数)

Hmm...昨天(可能是今天??)有人问我,一些静态和动态的问题,我列举一下:

静态区间K大数(排个序就好了)

静态区间K大数(主席树搞一搞)

待修改区间K大数(我就要讲的这个嘛)

动态区间和(zkw线段树/朴素线段树/2个树状数组)

静态区间和(...前缀和?)

步入正题

话说带修改区间K大数(注意是修改而并不是插入)

显然我们如果按照静态区间K大数的方法搞,暴力更新整棵线段树那么复杂度将是O(n log2 n)修改每次

想到单点修改求前缀和想到树状数组。不妨用树状数组维护线段树每个节点的前缀和,

换句话说要想求得每个节点具体的值,那么必须将属于这个节点的log2 n个节点的和全部累加,才是这个节点值域范围内,前缀插入数的个数

既然我们能求出在线段树某一节点值域范围内,前缀插入数的个数,我们就按照和静态区间K大数的类二分查找(用线段树对值域的二分代替整体二分)来找到一个对于的树根,输出他的标号就行。

所以,树状数组是维护一个数组表示的是要想知道当前每一节点值域是[L,R]前缀插入数的个数,是哪log2 n个节点的累加和。

这样子复杂度是O(log22n)每次插入。

查询的时候也是这样用R的前缀插入数的个数减去(l-1)前缀插入数的个数,就是该节点值域在[L,R]区间内插入数的个数。

这样的复杂度也是O(log22n)每次查询。

注意一些细节:

记录那几个点的数组(node_add和node_cut只需开log2n个即可),

然后离线离散化以后在线做(其实和在线效果一样),

然后离散化的时候尽量不用vector(不好习惯)

对tmp[]离散化,T记录离散化后下标

    sort(tmp+1,tmp+1+tmp[0]);
    T=unique(tmp+1,tmp+1+tmp[0])-tmp;

若要查询某个数val离散化以后是多少,那么就是

w=lower_bound(tmp+1,tmp+1+T,val)-tmp;

若要查询某个离散化后的数kkk实际上是多少那么直接访问下标

w=o[kkk];

还是得解释代码:

# include <cstdio>
# include <map>
# include <algorithm>
# include <vector>
# include <cstring>
using namespace std;
const int N=1e5+10;
# define lowbit(x) (x&(-x))
# define lson t[rt].ls,l,mid
# define rson t[rt].rs,mid+1,r
# define mid ((l+r)>>1)
int tmp[N<<1];
struct rec{
    int l,r,k,o;
}qes[N];
struct Seqment_Tree{
    int ls,rs,val;
}t[N*400];//树套树空间得开 n log n
int node_cut[25],node_add[25]; //这里只要log n个就行后面memset会慢
int cnt_cut,cnt_add,tot;
int root[N],a[N];
int T,n,m;
inline int read()
{
    int X=0,w=0; char c=0;
    while(c<‘0‘||c>‘9‘) {w|=c==‘-‘;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘) X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
void write(int x)
{
    if (x<0) x=-x,putchar(‘-‘);
    if (x>9) write(x/10);
    putchar(‘0‘+x%10);
}//I/O优化
void update(int &rt,int l,int r,int pos,int val)
{
    if (!rt) rt=++tot; //如果此时的访问空的节点那么建立节点,否则会覆盖掉原有节点信息(普通主席树最好也这么写,但是没必要,因为每个节点一定是空的!!!)
    t[rt].val+=val;
    if (l==r) return;
    if (pos<=mid) update(lson,pos,val);
    else update(rson,pos,val);
}//普通主席树的更改维护,值域+1/-1
void pre_update(int x,int val)
{
    int w=lower_bound(tmp+1,tmp+1+T,a[x])-tmp;
    for (int i=x;i<=n;i+=lowbit(i)) update(root[i],1,T,w,val);
}//首先处理出那几棵线段树管这个数组的位置的前缀和的,然后每个线段树分别维护
int query(int l,int r,int k)
{
    if (l==r) return l;
    int ret=0;
    for (int i=1;i<=cnt_add;i++)
        ret+=t[t[node_add[i]].ls].val;
    for (int i=1;i<=cnt_cut;i++)
        ret-=t[t[node_cut[i]].ls].val;
//该加的加(r),该减的减(l-1)
    if (k<=ret) {
        for (int i=1;i<=cnt_add;i++)
            node_add[i]=t[node_add[i]].ls;
        for (int i=1;i<=cnt_cut;i++)
            node_cut[i]=t[node_cut[i]].ls;
        return query(l,mid,k);
//不足右边不可能往左边搜
    } else {
        for (int i=1;i<=cnt_add;i++)
            node_add[i]=t[node_add[i]].rs;
        for (int i=1;i<=cnt_cut;i++)
            node_cut[i]=t[node_cut[i]].rs;
        return query(mid+1,r,k-ret);
//超过左边不可能往右边搜
    }
}
int pre_query(int l,int r,int k)
{
    memset(node_add,0,sizeof(node_add));
    memset(node_cut,0,sizeof(node_cut));
    cnt_add=cnt_cut=0;//清空
    for (int i=r;i;i-=lowbit(i)) node_add[++cnt_add]=root[i];
//处理该加的根节点
    for (int i=l-1;i;i-=lowbit(i)) node_cut[++cnt_cut]=root[i];
//处理该减的根节点
    return query(1,T,k);
}
int main()
{
    n=read();m=read();
    for (int i=1;i<=n;i++)
        tmp[++tmp[0]]=a[i]=read();
    for (int i=1;i<=m;i++) {
        char ch=0;
        while (ch!=‘Q‘&&ch!=‘C‘) ch=getchar();
        if (ch==‘Q‘) qes[i].l=read(),qes[i].r=read(),qes[i].k=read(),qes[i].o=1;
        else qes[i].l=read(),tmp[++tmp[0]]=qes[i].r=read(),qes[i].o=0;
    }
    sort(tmp+1,tmp+1+tmp[0]);
    T=unique(tmp+1,tmp+1+tmp[0])-tmp;
    for (int i=1;i<=n;i++) pre_update(i,1);
    for (int i=1;i<=m;i++) {
        if (qes[i].o==1) {
            write(tmp[pre_query(qes[i].l,qes[i].r,qes[i].k)]);
            putchar(‘\n‘);
        } else {
            pre_update(qes[i].l,-1);
            a[qes[i].l]=qes[i].r;
            pre_update(qes[i].l,1);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/ljc20020730/p/10357196.html

时间: 2024-10-16 06:22:22

【树状数组套主席树】带修改区间K大数的相关文章

BZOJ 3196 Tyvj 1730 二逼平衡树 ——树状数组套主席树

[题目分析] 听说是树套树.(雾) 怒写树状数组套主席树,然后就Rank1了.23333 单点修改,区间查询+k大数查询=树状数组套主席树. [代码] #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <set> #include <map> #include <string> #include <alg

[BZOJ 3196] 二逼平衡树 树状数组套主席树

3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3357  Solved: 1326[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为

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

Time:2016.05.09 Author:xiaoyimi 转载注明出处谢谢 传送门(权限) 题面 1901: Zju2112 Dynamic Rankings Time Limit: 10 Sec Memory Limit: 128 MB Submit: 6678 Solved: 2777 [Submit][Status][Discuss] Description 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a

[COGS257]动态排名系统 树状数组套主席树

257. 动态排名系统 时间限制:5 s   内存限制:512 MB [问题描述]给定一个长度为N的已知序列A[i](1<=i<=N),要求维护这个序列,能够支持以下两种操作:1.查询A[i],A[i+1],A[i+2],...,A[j](1<=i<=j<=N)中,升序排列后排名第k的数.2.修改A[i]的值为j.所谓排名第k,指一些数按照升序排列后,第k位的数.例如序列{6,1,9,6,6},排名第3的数是6,排名第5的数是9.[输入格式]第一行包含一个整数D(0<=

关于树状数组套主席树的一些博客

哇仿佛磕了几百年啊; 废话不多说,以下是帮助很大的一些blog: ZOJ 2112 Dynamic Rankings (动态第k大,树状数组套主席树) 主席树全纪录(这个很好) 主席树乱讲(没啥关系,不过有些题目可以刷??) 随笔分类 - 数据结构---主席树(同上) 原文地址:https://www.cnblogs.com/wwtt/p/10099695.html

【树套树】【树状数组套主席树】

这是你顾第一次写[树套树]!!!!!!!! [原题] 求区间第k小元素,区间可修改 [正解] 如果没有修改的话,就直接写搞个主席树利用前缀和加加减减一下就好了.但是多了个修改,修改以为着从当前修改节点k到往后n-k个树顶所代表的树全部都要修改,这是一件非常操蛋的事情.回想起多年前学数据结构初步的时候,区间批量修改无非就是树状数组or线段树.故我们借用树状数组的轮廓来构建主席树的各树顶. 对树状数组每个节点,我们都当成是主席树的树顶,改树顶所涵盖的区间与树状数组该节点意义相同. [查询]查询区间[

zoj 2112 Dynamic Rankings(树状数组套主席树)

题意:对于一段区间,每次求[l,r]的第k大,存在单点修改操作: 思路: 学习主席树参考: http://blog.csdn.net/wjf_wzzc/article/details/24560117(各种形式) http://blog.csdn.net/bossup/article/details/31921235(推荐) http://blog.csdn.net/xiaofengcanyuexj/article/details/25553521?utm_source=tuicool(图解)

浅谈树状数组套主席树

话说主席树还没写就先写这一篇了\(qwq\) 回顾一下主席树的实现过程:类似查分思想,将线段树的每次修改看做函数式以支持可持久化.因为这样的线段树是可减的. 那么我们维护信息的时候,就要维护每一次新形成的信息.但是我们可以根据前一个信息的基础上进行改动,而不必要去再建一棵树. 所以总而言之,是前缀和的思想. 那么,当需要修改的时候,怎么做呢? 考虑普通的区间操作,当做单点修改的时候,一般用树状数组,线段树和分块.最好实现的就是树状数组. 考虑用树状数组来维护主席树的信息. 树状数组中维护了每一次

【poj1901-求区间第k大值(带修改)】树状数组套主席树

901: Zju2112 Dynamic Rankings Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 7025  Solved: 2925[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]