题目链接:https://www.luogu.com.cn/problem/P2617

参考博客:https://blog.csdn.net/dreaming__ldx/article/details/80872728

在主席树的基础上实现单点更新也不困难,主要我们要明白主席树的函数性质,也就是一个根节点代表的信息我们可以认为是一段前缀,朴素主席树的T(i)树代表的是区间[1,i]的前缀,这样子单点更新之后就必须更新之后的T(i+1)~T(n)的线段树,这样的话时间复杂度非常高,我们可以利用主席树的函数性质,用树状数组套主席树,树状数组的C[i]点的主席树维护的是[i-lowbit(i)+1,i]区间的插入信息(虽然在根节点都是维护[1,n]区间),也就是T(i)树维护的是lowbit(i)长度的区间。每棵主席树维护的区间不是前缀区间,这个是树状数组套主席树的重点。所以更新的时候我们只要更新树状数组中的logn个结点,而在这每个结点之中我们需要修改一条链上的logn个结点(该结点属于主席树)。最终q次修改+m次查询的时间复杂度是O(q*log^2(n)+mlogn)。注意树状数组是建立在原数组的基础上的,所以树状数组中的索引与原数组的索引相关联,而主席树的索引则是离散值的索引,这与静态主席树相比又复杂了写,体现在add函数中,add函数传入的是实时原数组的索引,所以先修改位置的信息再通过这个位置获得修改的值的大小。

下面附上自写代码,结构体保存的树的信息,常数比较大:

 #include<bits/stdc++.h>
using namespace std;
typedef unsigned int ui;
typedef long long ll;
typedef unsigned long long ull;
#define pf printf
#define mem(a,b) memset(a,b,sizeof(a))
#define prime1 1e9+7
#define prime2 1e9+9
#define pi 3.14159265
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define scand(x) scanf("%llf",&x)
#define f(i,a,b) for(int i=a;i<=b;i++)
#define scan(a) scanf("%d",&a)
#define mp(a,b) make_pair((a),(b))
#define P pair<int,int>
#define dbg(args) cout<<#args<<":"<<args<<endl;
#define inf 0x3f3f3f3f
const int maxn=1e5+;
int n,m;
inline int read(){
int ans=,w=;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-;ch=getchar();}
while(isdigit(ch))ans=(ans<<)+(ans<<)+ch-'',ch=getchar();
return ans*w;
} struct node{
int l,r,sum;
}t[maxn*];
struct qu{
int a,b,c;
}q[maxn];
int cnt=,tot;
char s[];
//root中存放主席树的根节点编号,b中存放的所有将会出现在数列中的数
//b数组乘2是因为可能有1e5的原数据和1e5的更新点
//a中存放的是数组的实时动态情况,一旦有点修改就会体现在a数组上
int root[maxn*],b[maxn<<],a[maxn],qx[maxn],qy[maxn],ansx,ansy;
//在pre结点的基础上,建立now根结点并且在p位置加上v
//对每棵主席树的插入操作都是一样的,所以带修跟不带修的插入函数一样
int lowbit(int x){return x&(-x);}
void update(int l,int r,int pre,int &now,int p,int v)
{
t[++cnt]=t[pre];
now=cnt;
t[now].sum+=v;//(now结点的区间一定是包含p位置的)
if(l==r)return;
int m=l+r>>;//划分区间,决定向左子树还是右子树更新
if(p<=m)update(l,m,t[pre].l,t[now].l,p,v);
else update(m+,r,t[pre].r,t[now].r,p,v);
}
void add(int x,int v)//给第x个数插上v,这是在树套树的主席树上进行的加操作
{//对于树状数组i位置上的主席树,根就是root[i]
int k=lower_bound(b+,b+tot+,a[x])-b;
for(int i=x;i<=n;i+=lowbit(i))update(,tot,root[i],root[i],k,v);//就在第i棵树上进行操作
}
int query(int l,int r,int k)
{
if(l==r)return l;
int sum=,m=l+r>>;
f(i,,ansy)sum+=t[t[qy[i]].l].sum;
f(i,,ansx)sum-=t[t[qx[i]].l].sum;//计算[l,m]区间插入的数的数量
if(k<=sum)
{
f(i,,ansx)qx[i]=t[qx[i]].l;
f(i,,ansy)qy[i]=t[qy[i]].l;//同步、更新、递归
return query(l,m,k);
}
else
{
f(i,,ansx)qx[i]=t[qx[i]].r;
f(i,,ansy)qy[i]=t[qy[i]].r;
return query(m+,r,k-sum);
}
}
int main()
{
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
std::ios::sync_with_stdio(false);
n=read(),m=read(),cnt=,tot=;
f(i,,n){
a[i]=b[++tot]=read();
}
f(i,,m)
{
scanf("%s",s);
q[i].a=read(),q[i].b=read();
if(s[]=='Q')q[i].c=read();
else q[i].c=,b[++tot]=q[i].b;
//离线处理
}
sort(b+,b+tot+);
tot=unique(b+,b+tot+)-(b+);
f(i,,n)add(i,);//将第i个数插入主席树
f(i,,m)
{
if(q[i].c)
{
ansx=ansy=;
for(int j=q[i].b;j;j-=lowbit(j))qy[++ansy]=root[j];//把需要查询的点的根节点的编号保存下来
for(int j=q[i].a-;j;j-=lowbit(j))qx[++ansx]=root[j];
pf("%d\n",b[query(,tot,q[i].c)]);//注意vector中下标从1开始
}
else
{
add(q[i].a,-);
a[q[i].a]=q[i].b;//将点更新实时地在a上体现
add(q[i].a,);
}
}
}

带修主席树 洛谷2617 支持单点更新以及区间kth大查询的更多相关文章

  1. 【BZOJ-1146】网络管理Network DFS序 + 带修主席树

    1146: [CTSC2008]网络管理Network Time Limit: 50 Sec  Memory Limit: 162 MBSubmit: 3495  Solved: 1032[Submi ...

  2. P2617 Dynamic Rankings(带修主席树)

    所谓带修主席树,就是用树状数组的方法维护主席树的前缀和 思路 带修主席树的板子 注意数据范围显然要离散化即可 代码 #include <cstdio> #include <cstri ...

  3. 2018.07.01洛谷P2617 Dynamic Rankings(带修主席树)

    P2617 Dynamic Rankings 题目描述 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i ...

  4. 2018.07.01 BZOJ3295: [Cqoi2011]动态逆序对(带修主席树)

    3295: [Cqoi2011]动态逆序对 **Time Limit: 10 Sec Memory Limit: 128 MB Description 对于序列A,它的逆序对数定义为满足i<j& ...

  5. BZOJ1901 Dynamic Rankings|带修主席树

    题目链接:戳我 其实我并不会做,于是看了题解 我们都知道主席树是利用前缀和记录历史版本来搞区间K大的一种数据结构.不过一般的主席树只能搞定静态区间第K大.如果带修怎么办呢? 想一下...单点修改+区间 ...

  6. [luogu P2617] Dynamic Rankings 带修主席树

    带修改的主席树,其实这种,已经不能算作主席树了,因为这个没有维护可持久化的... 主席树直接带修改的话,由于这种数据结构是可持久化的,那么要相应改动,这个节点以后所有的主席树,这样单次修改,就达到n* ...

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

    1901: Zju2112 Dynamic Rankings Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 7292  Solved: 3038[Su ...

  8. Machine Learning Codeforces - 940F(带修莫队) && 洛谷P4074 [WC2013]糖果公园

    以下内容未验证,有错请指正... 设块大小为T,则块数为$\frac{n}{T}$ 将询问分为$(\frac{n}{T})^2$块(按照左端点所在块和右端点所在块分块),同块内按时间从小到大依次处理 ...

  9. HDU-1166 敌兵布阵 (树状数组模板题——单点更新,区间求和)

    题目链接 AC代码: #include<iostream> #include<cstdio> #include<cstring> #include<algor ...

随机推荐

  1. OpenStack入门

    云计算优势 降低成本,安全稳定,易扩展. 云计算三种服务模式 IaaS:基础设施即服务 IaaS(Infrastructure-as-a- Service):基础设施即服务.消费者通过Internet ...

  2. 对javascript EventLoop事件循环机制不一样的理解

    前置知识点: 浏览器原理,浏览器内核5种线程及协作,JS引擎单线程设计推荐阅读: 从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理 [FE]浏览器渲染引擎「内核」 js异步编程,Promise ...

  3. 关于PHPExcel的一些资料

    下面是总结的几个使用方法 include 'PHPExcel.php'; include 'PHPExcel/Writer/Excel2007.php'; //或者include 'PHPExcel/ ...

  4. 微软亚洲研究院的“哈利·波特”:Thomas Moscibroda

    在微软亚洲研究院,有一位名为Thomas Moscibroda的研究员几乎是无人不知.无人不晓,江湖人送外号"哈利·波特".Thomas认为他这么"红"是因为他 ...

  5. Flutter Widgets 之 ListWheelScrollView

    注意:无特殊说明,Flutter版本及Dart版本如下: Flutter版本: 1.12.13+hotfix.5 Dart版本: 2.7.0 基础用法 在展示大量数据的时候我们第一会想到使用ListV ...

  6. C++走向远洋——23(项目一,三角形,类)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:sanjiaoxing.cpp * 作者:常轩 * 微信公众号: ...

  7. PostgreSQL没有认证密码就登陆了缘由

    上午同事爆出这样的问题,使用正确的用户名和错误的密码连接了postgresql数据库,竟然连上了.这不是故意这样神操作,不小心密码写错了,咋一看这样怎么能行,随便输入一个密码都能登陆上.自己测试也是同 ...

  8. XXE学习(一)——XML基础

    XXE学习(一)——xml基础 一.XML简介 XML 指可扩展标记语言(EXtensible Markup Language) XML 是一种标记语言,很类似 HTML XML 的设计宗旨是传输数据 ...

  9. python之函数介绍

    # 函数 # 什么是函数: 能完成特定功能的工具,在Python中表示能完成特定功能的代码块.(函数定义) # 为什么要用函数 :①函数可以重复调用出来,效率高,而且维护成本低 ②使程序结构看起来清晰 ...

  10. vue项目用sha256、md5、base64加密密码

    无论你开发什么样的项目,你可能都会要开发登录.注册.修改密码.忘记密码这些功能,少数项目除外!!要实现这些功能,对于保护用户或者管理员账号密码,这是我们程序员肯定要做的事情.要是用户密码不加密,用明文 ...