ZJOI2013 K大数查询 和 LG3380【模板】二逼平衡树(树套树)
K大数查询
有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c;如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
N,M<=50000,N,M<=50000
sengxian的题解
一道树套树的题,外层是权值线段树,里层是普通区间线段树。
对于权值线段树的节点 \(u\) 表示权值区间 \([l, r)\),其对应的普通线段树的节点 \(v\) 表示序列\([l_1, r_1)\)中一共有多少个在权值区间 \([l, r)\) 的树。
这样不难得到我们的查询算法,要查 \([a, b]\) 的第 \(k\) 大,如果权值线段树根的右儿子代表的线段树区间 \([a, b]\) 的和为 \(s\),如果 \(s\) 大于 \(k\),说明第 \(k\) 大在右儿子代表的权值区间。否则在左儿子代表的权值区间上面。
修改也很好修改,只有一个区间加标记,如果要在 \([a, b]\) 中加一个 \(c\),那么应该在外层线段树中将所有包含权值 \(c\) 的节点对应的线段树的 \([a, b]\) 区间全部 +1。
剩下唯一的问题就是空间,理论上需要 \(O(n^2)\) 的空间,我们可以动态开点,未开的点给到 null,如果查询的时候走到 null,不需新建直接返回 0;如果修改的时候走到 null,那就新建节点,每次操作第一层最多影响 \(\log n\) 个节点,第二层最对影响 \(\log n\) 个节点,所以总空间复杂度是 \(O(m\log^2 n)\)
3.8 号新加入了一组嘿嘿嘿的数据,好多人挂了。注意到 \(n, m \le 50000\),那么最多可以加 \(2500000000\) 个节点!爆了 int,解决办法是换成 unsigned int!
指针版的写法不用考虑开空间的问题,较方便。
#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
	rg T data=0,w=1;
	rg char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') w=-1;
		ch=getchar();
	}
	while(isdigit(ch))
		data=data*10+ch-'0',ch=getchar();
	return data*w;
}
template<class T>il T read(rg T&x){
	return x=read<T>();
}
typedef long long ll;
using namespace std;
int n,m,vn,a,b,v;
#define mid ((l+r)/2)
struct SegNode*null;
struct SegNode{
	SegNode*ls,*rs;
	ll sum,addv;
	SegNode():ls(null),rs(null),sum(0),addv(0){}
	void maintain(int l,int r){
		if(r-l==1) sum=addv;
		else sum=ls->sum+rs->sum+addv*(r-l);
	}
};
void modify(SegNode*&o,int l,int r){
	if(l>=b||r<=a) return;
	if(o==null) o=new SegNode();
	if(l>=a&&r<=b) o->addv++;
	else modify(o->ls,l,mid),modify(o->rs,mid,r);
	o->maintain(l,r);
}
ll query(SegNode*&o,int l,int r,ll add=0){
	if(l>=b||r<=a) return 0;
	if(l>=a&&r<=b) return o->sum+add*(r-l);
	return query(o->ls,l,mid,add+o->addv)+query(o->rs,mid,r,add+o->addv);
}
struct SegNode2D*null2D;
struct SegNode2D{
	SegNode2D*ls,*rs;
	SegNode*val;
	SegNode2D():ls(null2D),rs(null2D),val(null){}
}*root;
void modify2D(SegNode2D*&o,int l,int r){
	if(o==null2D) o=new SegNode2D();
	if(r-l>1){
		if(v<mid) modify2D(o->ls,l,mid);
		else modify2D(o->rs,mid,r);
	}
	modify(o->val,0,n);
}
ll query2D(SegNode2D*o,int l,int r,int k){
	if(r-l==1) return l;
	ll s=query(o->rs->val,0,n);
	if(k<=s) return query2D(o->rs,mid,r,k);
	else return query2D(o->ls,l,mid,k-s);
}
void init_null() { // edit 1: pointer init
    null = new SegNode(), null->ls = null, null->rs = null, null->sum = null->addv = 0;
    null2D = new SegNode2D(), null2D->ls = null2D, null2D->rs = null2D, null2D->val = null;
    root = null2D;
}
co int maxm=5e4+1;
vector<int>cs;
int opers[maxm],as[maxm],bs[maxm],vs[maxm];
int compress(){
	for(int i=0;i<m;++i){
		read(opers[i]),read(as[i]),read(bs[i]),read(vs[i]);
		if(opers[i]==1) cs.push_back(vs[i]);
	}
	sort(cs.begin(),cs.end()),cs.erase(unique(cs.begin(),cs.end()),cs.end());
	for(int i=0;i<m;++i) if(opers[i]==1)
		vs[i]=lower_bound(cs.begin(),cs.end(),vs[i])-cs.begin();
	return cs.size();
}
int main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	init_null();
	read(n),read(m);
	vn=compress();
	for(int i=0;i<m;++i){
		int oper=opers[i];
		a=as[i]-1,b=bs[i],v=vs[i];
		if(oper==1) modify2D(root,0,vn);
		else if(oper==2) printf("%d\n",cs[query2D(root,0,vn,v)]);
		else assert(0);
	}
	return 0;
}
二逼平衡树(树套树)
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
- 查询k在区间内的排名
- 查询区间内排名为k的值
- 修改某一位值上的数值
- 查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647)
- 查询k在区间内的后继(后继定义为严格大于x,且最小的数,若不存在输出2147483647)
注意上面两条要求和tyvj或者bzoj不一样,请注意
说明
时空限制:2s,128M
\(n,m \leq 5\cdot {10}^4\)
保证有序序列所有值在任何时刻满足$ [0, {10} ^8]$
树状数组套权值线段树,时空复杂度\(O((n+m) \log_2^2n)\)
#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
#define lowbit(x) (x&-x)
template<class T>il T read(){
	rg T data=0,w=1;
	rg char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') w=-1;
		ch=getchar();
	}
	while(isdigit(ch))
		data=data*10+ch-'0',ch=getchar();
	return data*w;
}
template<class T>il T read(rg T&x){
	return x=read<T>();
}
typedef long long ll;
using std::vector;
vector<int> vs;
co int N=5e4+1,LG=120;
int c[N],n,m;
struct quiz{
	int opt,l,r,k;
}q[N];
// Tree Tao Tree
namespace T{
	// Interval Tree
	int L[N*LG],R[N*LG],sum[N*LG];
	int tot,root[N];
	int rank(vector<int>&x,int l,int r,int p){
		if(l==r) return 1;
		int m=(l+r)/2;
		if(p<=m){
			for(int i=0;i<x.size();++i)
				x[i]=x[i]<0?-L[-x[i]]:L[x[i]];
			return rank(x,l,m,p);
		}
		else{
			int s=0;
			for(int i=0;i<x.size();++i){
				x[i]<0?s-=sum[L[-x[i]]]:s+=sum[L[x[i]]];
				x[i]=x[i]<0?-R[-x[i]]:R[x[i]];
			}
			return s+rank(x,m+1,r,p);
		}
	}
	int kth(vector<int>&x,int l,int r,int k){
		if(l==r) return l;
		int s=0;
		for(int i=0;i<x.size();++i)
			x[i]<0?s-=sum[L[-x[i]]]:s+=sum[L[x[i]]];
		int m=(l+r)/2;
		if(s>=k){
			for(int i=0;i<x.size();++i)
				x[i]=x[i]<0?-L[-x[i]]:L[x[i]];
			return kth(x,l,m,k);
		}
		else{
			for(int i=0;i<x.size();++i)
				x[i]=x[i]<0?-R[-x[i]]:R[x[i]];
			return kth(x,m+1,r,k-s);
		}
	}
	void change(int&x,int l,int r,int p,int d){
		if(!x) x=++tot;
		sum[x]+=d;
		if(l==r) return;
		int m=(l+r)/2;
		if(p<=m) change(L[x],l,m,p,d);
		else change(R[x],m+1,r,p,d);
	}
	int precessor(vector<int>&x,int l,int r,int p){
		if(p==1) return -1;
		vector<int>y;
		int k=rank(y=x,l,r,p);
		if(k==1) return -1;
		return kth(y=x,l,r,k-1);
	}
	int successor(vector<int>&x,int l,int r,int p){
		if(p==vs.size()) return -1;
		vector<int>y;
		int k=rank(y=x,l,r,p+1)-1,s=0; // edit 1: reuse rank
		for(int i=0;i<x.size();++i)
			x[i]<0?s-=sum[-x[i]]:s+=sum[x[i]];
		if(k==s) return -1;
		return kth(y=x,l,r,k+1);
	}
	// Binaru Index Tree
	int rank(int l,int r,int v){
		vector<int> roots;
		for(int i=r;i;i-=lowbit(i))
			roots.push_back(root[i]);
		for(int i=l-1;i;i-=lowbit(i))
			roots.push_back(-root[i]);
		return rank(roots,1,vs.size(),v);
	}
	int kth(int l,int r,int k){
		vector<int> roots;
		for(int i=r;i;i-=lowbit(i))
			roots.push_back(root[i]);
		for(int i=l-1;i;i-=lowbit(i))
			roots.push_back(-root[i]);
		return kth(roots,1,vs.size(),k);
	}
	void change(int p,int v){
		for(int i=p;i<=n;i+=lowbit(i)){
			change(root[i],1,vs.size(),c[p],-1); // edit 1:c[p]
			change(root[i],1,vs.size(),v,1);
		}
		c[p]=v;
	}
	int precessor(int l,int r,int v){
		vector<int> roots;
		for(int i=r;i;i-=lowbit(i))
			roots.push_back(root[i]);
		for(int i=l-1;i;i-=lowbit(i))
			roots.push_back(-root[i]);
		return precessor(roots,1,vs.size(),v);
	}
	int successor(int l,int r,int v){
		vector<int> roots;
		for(int i=r;i;i-=lowbit(i))
			roots.push_back(root[i]);
		for(int i=l-1;i;i-=lowbit(i))
			roots.push_back(-root[i]);
		return successor(roots,1,vs.size(),v);
	}
}
int main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	read(n),read(m);
	for(int i=1;i<=n;++i)
		vs.push_back(read(c[i]));
	for(int i=1;i<=m;++i){
		read(q[i].opt);
		if(q[i].opt!=3){
			read(q[i].l),read(q[i].r),read(q[i].k);
			if(q[i].opt!=2) vs.push_back(q[i].k);
		}
		else read(q[i].l),vs.push_back(read(q[i].k));
	}
	sort(vs.begin(),vs.end()),vs.erase(unique(vs.begin(),vs.end()),vs.end());
	for(int i=1;i<=n;++i){
		c[i]=lower_bound(vs.begin(),vs.end(),c[i])-vs.begin()+1;
		for(int j=i;j<=n;j+=lowbit(j)) // insert
			T::change(T::root[j],1,vs.size(),c[i],1);
	}
	for(int i=1;i<=m;++i){
		if(q[i].opt!=2) q[i].k=lower_bound(vs.begin(),vs.end(),q[i].k)-vs.begin()+1; // edit 2: only operator 3
		if(q[i].opt==1) printf("%d\n",T::rank(q[i].l,q[i].r,q[i].k));
		else if(q[i].opt==2) printf("%d\n",vs[T::kth(q[i].l,q[i].r,q[i].k)-1]);
		else if(q[i].opt==3) T::change(q[i].l,q[i].k);
		else if(q[i].opt==4){
			int re=T::precessor(q[i].l,q[i].r,q[i].k);
			if(re==-1) puts("-2147483647");
			else printf("%d\n",vs[re-1]);
		}
		else if(q[i].opt==5){
			int re=T::successor(q[i].l,q[i].r,q[i].k);
			if(re==-1) puts("2147483647");
			else printf("%d\n",vs[re-1]);
		}
	}
	return 0;
}
ZJOI2013 K大数查询 和 LG3380【模板】二逼平衡树(树套树)的更多相关文章
- BZOJ 3110: [Zjoi2013]K大数查询 [树套树]
		3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 6050 Solved: 2007[Submit][Sta ... 
- 树套树专题——bzoj 3110: [Zjoi2013] K大数查询 & 3236 [Ahoi2013] 作业 题解
		[原题1] 3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec Memory Limit: 512 MB Submit: 978 Solved: 476 Descri ... 
- bzoj 3110: [Zjoi2013]K大数查询 树状数组套线段树
		3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1384 Solved: 629[Submit][Stat ... 
- BZOJ 3110: [Zjoi2013]K大数查询( 树状数组套主席树 )
		BIT+(可持久化)权值线段树, 用到了BIT的差分技巧. 时间复杂度O(Nlog^2(N)) ---------------------------------------------------- ... 
- BZOJ 3110([Zjoi2013]K大数查询-区间第k大[段修改,在线]-树状数组套函数式线段树)
		3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec Memory Limit: 512 MB Submit: 418 Solved: 235 [ Submit][ ... 
- BZOJ_3110_[Zjoi2013]K大数查询_整体二分+树状数组
		BZOJ_3110_[Zjoi2013]K大数查询_整体二分+树状数组 Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位 ... 
- P3332 [ZJOI2013]K大数查询(线段树套线段树+标记永久化)
		P3332 [ZJOI2013]K大数查询 权值线段树套区间线段树 把插入的值离散化一下开个线段树 蓝后每个节点开个线段树,维护一下每个数出现的区间和次数 为了防止MLE动态开点就好辣 重点是标记永久 ... 
- 【BZOJ3110】【LG3332】[ZJOI2013]K大数查询
		[BZOJ3110][LG3332][ZJOI2013]K大数查询 题面 洛谷 BZOJ 题解 和普通的整体分治差不多 用线段树维护一下每个查询区间内大于每次二分的值\(mid\)的值即可 然后再按套 ... 
- 3110: [Zjoi2013]K大数查询
		3110: [Zjoi2013]K大数查询 https://lydsy.com/JudgeOnline/problem.php?id=3110 分析: 整体二分+线段树. 两种操作:区间加入一个数,区 ... 
随机推荐
- PHP学习(4)——数组的使用
			1.数组的概念 数组就是一个用来存储一系列变量值的命名区域. 每个数组元素有一个相关的索引(也成为关键字),它可以用来访问元素. PHP允许间隔性地使用数字或字符串作为数组的索引. 2.数字索引数组 ... 
- python tarfile模块
			TarFile类对于就是tar压缩包实例. 其由member块组成, member块则包括header块和data块. 每个member以TarInfo对象形式描述. 所以TarFile就是TarIn ... 
- csv文件的读取写法 from Udacity
			长版本 import unicodecsv enrollments_filename = 'C:\\Users\\xxxxx\\Desktop\\try.csv' enrollments = [] f ... 
- .NET 表达式计算:Expression Evaluator
			Expression Evaluator 是一个轻量级的可以在运行时解析C#表达式的开源免费组件.表达式求值应该在很多地方使用,例如一些工资或者成本核算系统,就需要在后台动态配置计算表达式,从而进行计 ... 
- 使用django连接数据库 对数据库 增删改查
			如果路由访问的时候出现 就把项目中的注释掉 登录功能 1 路由访问如果不加斜杠 会内部自动重定向加斜杠的路由 所有的静态文件(css,js,前端第三方类库)默认都放在static文件下 #静态文件配置 ... 
- 了解WebSocket
			了解WebSocket  WebSocket协议是基于TCP的一种新的协议.WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符.它实现了浏览器与服务器全 ... 
- Codeforces 1237E. Balanced Binary Search Trees
			传送门 这一题是真的坑人,时间空间都在鼓励你用 $NTT$ 优化 $dp$...(但是我并不会 $NTT$) 看到题目然后考虑树形 $dp$ ,设 $f[i][0/1]$ 表示 $i$ 个节点的树,根 ... 
- 不同主机的docker内容器通过直接路由的方式进行通信
			引用文章链接:https://www.cnblogs.com/xiao987334176/p/10049844.html 六.操作总结 修改不同主机上docker默认的网络参数 主机1:192.168 ... 
- Java object-oriented8/5
			package Chapter1.Class;/** * 制作一个简单的通讯录.. * @author LENOVO * */public class ClassDemo_01 { String na ... 
- luogu题解 P3709 【大爷的字符串题】
			题目链接: https://www.luogu.org/problemnew/show/P3709 思路: 首先我是没读懂题目的,浏览了讨论区的dalao发现才知道就是求区间众数的出现次数. 然后肯定 ... 
