POJ2104 区间第k大(带修改)

学习笔记: 进阶版状数组(区间修改区间查询以及查询第K元素) 状数组相信家都很熟悉了,而今天我将会为了一些更加全面的操作,并且欢迎补充哦。 其实我想说的是,除了RMQ,线段能做的,状数组都能做。 换句话说,这是一个稍微进阶版的的状数组,读者至少要会单调修改区间查询这个最基本的操作。 状数组应该算是常数非常小的数据结构啦。而小生特别喜欢这个数据结构,这是因为它特别短,就是又短又快! 核心操作:lowbit int ... 阅读详情

Description

  给定一个长度为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。

Input

  输入文件的第一行包含两个整数N和M,分别表示序列的长度为N和有M个操作。
  接下来的N个不大于10^9正整数,第i个表示序列A[i]的初始值。
  然后的M行,每行为一个操作Q i j k 或者C i j分别表示查询A[i],A[i+1],A[i+2],…,A[j](1<=i<=j<=N)中,升序排列后排名第k的数和修改A[i]的值为j。

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

Hint

【数据范围】:
  20%的数据中,m,n≤100; 
  40%的数据中,m,n≤1000; 
  100%的数据中,m,n≤10000。

打一发树套树,表示splay的哪个旋转到根下面的写法好迷。。

树套树常数贼大= =主席树貌似好用不少(还没复习233)

#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
#define Red(i,r,L) for(register int i=(r);i>=(L);--i)
const int N = 1e4+10,Maxsiz = N*log2(N)*2;
int n,m,a[N];
struct Splay{
	int p[Maxsiz],ch[Maxsiz][2];
	int cnt,k[Maxsiz],siz[Maxsiz];
	#define Ls(v) ch[v][0]
	#define rs(v) ch[v][1]
	#define sum(v) siz[v]=siz[Ls(v)]+siz[rs(v)]+1
	inline void rotate(int x){
		int f=p[x],gf=p[f],tp=rs(f)==x,son=ch[x][!tp];
		ch[p[son]=f][tp]=son,sum(f);
		ch[p[f]=x][!tp]=f,sum(x);
		ch[p[x]=gf][rs(gf)==f]=x;
	}
	inline void splay(int x,int &rt){
		while(p[x]){
			if((p[p[x]])&&((rs(p[p[x]])==p[x])==(rs(p[x])==x)))rotate(p[x]);
			rotate(x);
		}
		rt=x;
	}
	inline void newnode(int x,int v,int Fa){
		siz[x]=1;
		p[x]=Fa;
		k[x]=v;
	}
	inline void insert(int &x,int v,int Fa,int &root){
		if(!x)return newnode(x=++cnt,v,Fa),splay(x,root),void();
		insert(ch[x][k[x]<v],v,x,root);
	}
	inline int Query(int x,int v){
		int tot=0;
		while(x)k[x]<=v?tot+=siz[Ls(x)]+1,x=rs(x):x=Ls(x);
		return tot;
	}
	inline int findpre(int x){
		for(x=Ls(x);rs(x);x=rs(x));
		return x;
	}
	inline int findsub(int x){
		for(x=rs(x);Ls(x);x=Ls(x));
		return x;
	}
	inline void delet(int x,int v,int &root){
		for(;k[x]^v;x=ch[x][k[x]<v]);
		splay(x,root);
		if(!Ls(x))return p[root=rs(x)]=0,void();
		int pre=findpre(x);
		splay(pre,root);
		rs(pre)=rs(x);
		if(rs(x))p[rs(x)]=pre;
		sum(pre);
	}
}sp;
struct SegMent{
	struct tree{
		int L,r,rt;
	}t[N<<2];
	#define Lc v<<1
	#define rc v<<1|1
	inline void Modify(int v,int x,bool cmd){
		if(t[v].L>x||t[v].r<x)return ;
		cmd?sp.insert(t[v].rt,a[x],0,t[v].rt):sp.delet(t[v].rt,a[x],t[v].rt);
		if(t[v].L==t[v].r)return ;
		Modify(Lc,x,cmd),Modify(rc,x,cmd);
	}
	inline int Query(int v,int A,int b,int k){
		if(t[v].L>b||t[v].r<A)return 0;
		if(A<=t[v].L&&t[v].r<=b)return sp.Query(t[v].rt,k);
		return Query(Lc,A,b,k)+Query(rc,A,b,k);
	}
	inline void build(int v,int L,int r){
		t[v]=(tree){L,r};
		Inc(i,L,r)sp.insert(t[v].rt,a[i],0,t[v].rt);
		if(L==r)return ;
		int Mid=L+r>>1;
		build(Lc,L,Mid),build(rc,Mid+1,r);
	}
}tr;
inline void init(){
	scanf("%d%d",&n,&m);
	Inc(i,1,n)scanf("%d",&a[i]);
	tr.build(1,1,n);
}
inline void Modify(){
	int x,k;scanf("%d%d",&x,&k);
	tr.Modify(1,x,0);
	a[x]=k;
	tr.Modify(1,x,1);
}
inline void Query(){
	int ans,L,r,kth;
	scanf("%d%d%d",&L,&r,&kth);
	for(int x=tr.t[1].rt;x;){
		int tmp=tr.Query(1,L,r,sp.k[x]);
		if(tmp<kth)x=sp.rs(x);
		else ans=sp.k[x],x=sp.Ls(x);
	}
	cout<<ans<<"\n";
}
inline void solv(){
	Inc(i,1,m){
		char c=getchar();while((c^'Q')&&(c^'C'))c=getchar();
		c=='Q'?Query():Modify();
	}
}
int main(){
	init();
	solv();
	return 0;
}

补一个主席树,,

和静态版本略有不同(我感觉完全不一样。。),说的可能有点乱,大概是那个意思。

查询区间[l,r],由于主席树的前缀和性质想到树状数组,常规的树状数组直接改点值,我们不过是把点值换成了整棵线段树。

简单地说,就是树状数组套上多棵动态开点的权值线段树。

但是这样写就不满足主席树只加点不删除的性质。。我也很迷~主席树的定义到底是个什么= =

再补:满足前缀和性质的线段树就是主席树~

#include<bits/stdc++.h>
using namespace std;
const int Maxn=10005;
int n,a[Maxn];
struct SegMent{
	struct tree{
		int ls,rs,siz;
	}t[Maxn*40];int cnt;
	inline void modify(int &x,int v,int l,int r,int cmd){
		if(l>v||r<v)return ;
		t[x?:x=++cnt].siz+=cmd;
		if(l==r)return ;
		int mid=l+r>>1;
		modify(t[x].ls,v,l,mid,cmd),modify(t[x].rs,v,mid+1,r,cmd);
	}
}seg;
struct BIT{
	int root[Maxn];
	int q[2][Maxn];
	#define lowbit(x) (x)&-(x)
	inline void update(int x,int k,int cmd){
		for(;x<=n;x+=lowbit(x))seg.modify(root[x],k,0,(1<<30),cmd);
	}
	inline void getroot(int l,int r){
		for(q[0][0]=0;l>0;l-=lowbit(l))q[0][++q[0][0]]=root[l];
		for(q[1][0]=0;r>0;r-=lowbit(r))q[1][++q[1][0]]=root[r];		
	}
	inline int sum(bool t){
		int ret=0;
		for(int i=1;i<=q[t][0];++i)ret+=seg.t[seg.t[q[t][i]].ls].siz;
		return ret;
	}
	inline int query(int l,int r,int k){
		if(l==r)return l;
		int size=sum(1)-sum(0);
		int mid=l+r>>1;
		if(size>=k){
			for(int i=1;i<=q[0][0];++i)q[0][i]=seg.t[q[0][i]].ls;
			for(int i=1;i<=q[1][0];++i)q[1][i]=seg.t[q[1][i]].ls;
			return query(l,mid,k);
		}else {
			for(int i=1;i<=q[0][0];++i)q[0][i]=seg.t[q[0][i]].rs;
			for(int i=1;i<=q[1][0];++i)q[1][i]=seg.t[q[1][i]].rs;
			return query(mid+1,r,k-size);
		}
	}
}bit;
int main(){
	int m;scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)scanf("%d",&a[i]);
	for(int i=1;i<=n;++i)bit.update(i,a[i],1);
	for(int i=1;i<=m;++i){
		char c=getchar();while(!isalpha(c))c=getchar();
		if(c=='C'){
			int x,k;scanf("%d%d",&x,&k);
			bit.update(x,a[x],-1);
			bit.update(x,a[x]=k,1);
		}else {
			int l,r,k;scanf("%d%d%d",&l,&r,&k);
			bit.getroot(l-1,r);
			printf("%d\n",bit.query(0,(1<<30),k));
		}
	}
	return 0;
}

 

区间第K值修改 分块 区间第K值修改 Q:给定一个序列 1、查询一个L到R的区间内的第K(小)值。2、修改一个值。 这道神题困扰了我很久,直到学了各种各样的算法才会会这道题。 先比较一下流行的算法的效率吧 对于一个长度为N的序列 暴力:对于一段查询区间每次进行快排。 时间复杂度: 单次查询:O(NlogN) 修改:O(1) 空间复杂度:O(N) 线段套平衡: 单次查询:O(log^3 N) 阅读详情

相关推荐

区间修改区间查询

区间修改区间查询

11 321

区间第k的几种解法

区间第K问题,变化包括修改和不修改,强制在线和允许离线 修改主要是单点修改,我们前面也只讨论这种情况。 接下来我们从编程复杂度和时空复杂度来讨论几种做法。 1.整体二分(编程复杂度:低-中,时间复杂度:优秀,空间复杂度:优秀) 缺点:只能做离线 优点:空间都是O(n)。常数小。修改O(nlog2n),不修改O(nlogn)。 但是不修改的情况,如果允许的话,...

weixin_30564785的博客 725

区间元素可变(添加与删除)的第求k数和给定数的排名问题

思想来源于张昆玮的《统计的力量》 划分只能在静态区间上求第k数和给定数的排名 基于字母数的动态性和数值小的字典有序性,只要在其结点上存以当前节点结尾的串(数的二进制形式)的数目,和以该串为前缀的串的数目,便可在位模式长度时间内进行元素增添、删除和以上问题的求解。但当元素数值过,可能出现时间常数过的问题,并且空间需求不易估计。

qwe20060514的专栏 777

poj2104】不修改区间第k 主席

【题目意】 给定一个长为N的序列,每个序列的权值为Ai.有M个询问,每个询问为(L,R,K),分别代表[L,R]的第K的数。 期中n=100000,m=5000 【题解】 用主席解决,那么什么是主席呢? 首先我们先明确一下权值线段的概念。 平常我们用的线段都是区间线段,而权值线段和平衡的结点意义是类似的。 权值线段中(下文所说线段均值权值线段),每个结

chty_syq的博客 375

[POJ 2104]K-th Number (修改区间k,函数式线段)

/* http://hi.baidu.com/wyl8899/item/e00796a9cb2df73d020a4d68 */ #include #include #include using namespace std; const int MAX_N=100005; const int MAX_M=5005; const int MAX=MAX_M*600; int n,m; int

Yukar_syt的专栏 910

POJ-2104 K-th Number (主席修改区间第k)

主席 入门题。题意:1e5个数,5e4次询问,每次询问区间第k

hmc0411的博客 532

区间第kpoj 2104

#include #include #include #include #define maxn 1000005 using namespace std; vector dat[maxn*2]; int num[maxn],A[maxn]; int build (int o,int L,int R){ if (L==R){ dat[o].push_back(num[L]);

LHYL 846

poj2104区间第k(版本1)

POJ2104区间第k Description 给定一个长度为n的序列,m个询问,每个询问的形式为:L,r,k表示在[L,r]间中的第k元素。 Input 第1行:2个数,n,m表示序列的长度和询问的个数 第2行:n个数,表示n个数的小 第3-m+2行:每行3个数,L,r,k表示询问在[L,r]区间内第k小的元素

袋鼠与蒟蒻 438

区间第KPOJ-2104

可持久化线段不是一颗完全二叉,所以不能用层次序编号,而应该直接记录每个节点的左、右子节点的编号。可持久化线段维护了每次操作后线段的历史形态。 下面举个简单的例子来理解一下: 以poj-2104为例;这棵是一颗不用修改的线段 题意:即多次查询查询区间第K的数。 POJ-2104 (区间第k) //主席的学习--区间第k //区间第k又可以用整体分治算法,归并,线段套平衡...

小` pi孩 280

主席-区间第k值(不修改

题目:poj2104 && hdu2665 题意:求区间第K的值。 分析:资料1  主席包含n棵线段,这n棵线段的形状完全相同。而且之间有很的重叠。 线段root[i]表示数组a中区间[1,i]的元素插进线段时的版本。 那么再添加一个元素a[i+1]时,只需修改线段上的从根节点开始向下走的一条路径。 那么对于线段root[i+1],我们先“拷贝”线段root[

w20810的专栏 2140

POJ 2104 K-th Number【整体二分 + 状数组】

本来只是想学一下CDQ,还是先把整体二分搞懂一点。这题窝几个月前分别用划分树套树主席和挑战上介绍的分桶法实现了一发(然而现在都忘得差不多了) 最快的是划分,其次是主席,然后是树套树,还有一个10000+ms分桶法的思想实现的,最上面的就是整体二分的方法。题目链接:http://poj.org/problem?id=2104题意:给定序列,求每次询问区间的第K数。分析:首先介绍整体

Tuesday 8098

poj 2104 可持久化线段 区间K修改

poj 2104 区间k修改查询 模板 #include #include #include #include #include #include #include #define N 240008 #define M 5500000 using namespace std; int n,m,rt[N]; struct Node{ int l,r,cnt;; }t

我的博客 293

poj 2104 区间第k 主席

/* 静态主席区间第K poj 2104 主要思想: 1. 将数据离散化 2. 用线段来维护信息,维护处于该区间的数的个数 3. 将原有数组中的每一个数依次插入,每次插入一个值的时候新建一棵线段 这样就有了n+1棵线段,对于询问(l,r,k),只需查询T[l-1]和T[r]两棵线段

legedexinshi的专栏 569

POJ 2104 可持久化线段区间第k

K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 35455   Accepted: 11296 Case Time Limit: 2000MS Description You are working for Macrohard com

_rabbit的专栏 1406

【划分】求区间K

<br />       求区间K数,众所周知有二分答案+树套树的做法,每个询问复杂度为O(log3N),可谓近似一个O(N)了,不仅时间复杂度高,编程复杂度也不低,后来才发现还有一种叫划分的数据结构,专门做这种问题。<br />      本来觉得知道个树套树就够了,据我所知划分还不支持区间修改操作,但是这次NOI一试的piano偏偏那出题人就有这么重口味,给你来两个500000的点!真是不简单,当时我都在想有没有O(N)的算法了。树套树的复杂度偏偏就被卡掉了,三个点平均每个点要跑5秒,好吧,那就学

Evil.livE 4381

整体二分初探 两类区间第K问题 poj2104 & hdu5412

看到好多讲解都把整体二分和$CDQ$分治放到一起讲 不过自己目前还没学会$CDQ$分治 就单独谈谈整体二分好了 先推荐一下$XHR$的 <浅谈数据结构题的几个非经典解法> 整体二分在当中有较为详细的讲解 先来说一下静态第$K$小的整体二分解法 $(POJ2104)$ 题目链接:http://poj.org/problem?id=2104 所谓整体二分 就是利用所有的询问相...

weixin_30621919的博客 138

POJ2104 K-th Number —— 区间第k小 整体二分

题目链接:https://vjudge.net/problem/POJ-2104 K-th Number Time Limit:20000MS Memory Limit:65536K Total Submissions:64110 Accepted:22556 Case Time Limit:2000MS ...

alince20008的博客 107

整体二分求区间第k模板(POJ 2104

自己的模板,线段实现的,可以参考一下 算法步骤 参考一下文章 http://www.cnblogs.com/dirge/articles/5810855.html https://www.cnblogs.com/rayrayrainrain/articles/6477611.html //#include<bits/stdc++.h> #include&l...

Bowen的博客 550
上一篇: USACO 2009 January Gold 安全路径
下一篇: BZOJ3196 二逼平衡树
DancingZ
博客等级 码龄9年 10粉丝 137原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值