首页 > 其他分享 >BLOG1029<-主席树,

BLOG1029<-主席树,

时间:2023-10-29 12:12:26浏览次数:37  
标签:count ch shu hao 线段 value BLOG1029 主席

这个比splay好学多了(

主席树就是把每次修改的版本保留下来,版本就是线段树曾经的一个状态。

如果打暴力的话可以想把每个状态的线段树都保留下来,炸飞了。

主席树单点修改的话就是发现了每次修改只改了包含这个点的层,线段树上,这是 \(\log n\) 级的,我们可以只创建这些新节点。每次修改我们就重新设置一个根,这个根只领导了 \(\log\) 的新节点,其他的都连接到原树上不被修改的点。

图:

image

这个新的链顶就是新的一个根,这个链可以访问到一个完整的常规的线段树,和原本的根(好比说是1吧),是一样的。

时间和空间复杂度都是 \(O(n\log n)\) 级别

可持久化线段树1
#include<bits/stdc++.h>
typedef int count ;
typedef long long value;
inline value read(){
	count f(1);
	value x(0);
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
	return f*x;
}
void write(value x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
#define debug(x) std::cout<<"###"<<x<<std::endl;

#define maxn 1000010
count n,m,all;
count rt[maxn];
value a[maxn];

struct tree{
	count l,r,ls,rs;
	count data;
} shu[maxn<<5];
inline void add(count &hao){
	if(!hao){
		hao=++all;
	}
}
inline void copy(count &hao){
	shu[++all]=shu[hao];
	hao=all;
}
inline void pushup(count hao){
	shu[hao].data=shu[shu[hao].ls].data+shu[shu[hao].rs].data;
}
void bt(count &hao,count l,count r){
	add(hao);
	shu[hao].l=l,shu[hao].r=r;
	if(l==r){
		shu[hao].data=a[l];
		return ;
	}
	count mid=(l+r)>>1;
	bt(shu[hao].ls,l,mid);
	bt(shu[hao].rs,mid+1,r);
	pushup(hao);
}
void update(count &hao,count pos,count val){
	copy(hao);
	if(shu[hao].l==shu[hao].r){
		shu[hao].data=val;
		return ;
	}
	count mid=(shu[hao].l+shu[hao].r)>>1;
	if(pos<=mid) update(shu[hao].ls,pos,val);
	else update(shu[hao].rs,pos,val);
	pushup(hao);
}
value ask(count hao,count pos){
	if(!hao) return 0;
	if(shu[hao].l==shu[hao].r){
		return shu[hao].data;
	}
	count mid=(shu[hao].l+shu[hao].r)>>1;
	value val=0;
	if(pos<=mid) val+=ask(shu[hao].ls,pos);
	else val+=ask(shu[hao].rs,pos);
	return val; 
}

int main(){
	n=read(),m=read();
	for(count i=1;i<=n;i++){
		a[i]=read();
	}
	
	bt(rt[0],1,n);
	
	for(count i=1;i<=m;i++){
		count ver=read();
		count op=read();
		
		if(op==1){
			count pos=read(),val=read();
			rt[i]=rt[ver];
			update(rt[i],pos,val);
		}
		else{
			count pos=read();
			write(ask(rt[ver],pos)),putchar('\n');
			rt[i]=rt[ver];
		}
	}
	return 0;
}

经典问题:查 \([l,r]\) 中第 \(k\) 小。

前缀和+权值线段树。

查 \(l-1\) 和 \(r\) 版本的权值线段树,维护同一值域的部分可以相加减,差即为这一区间的信息。剩下按权值线段树常规做就行。

可持久化线段树2
#include<bits/stdc++.h>
typedef int count ;
typedef long long value;
inline value read(){
	count f(1);
	value x(0);
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
	return f*x;
}
void write(value x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
#define debug(x) std::cout<<"###"<<x<<std::endl;

#define maxn 200010
count n,m;
value a[maxn],book[maxn];
count rt[maxn],all;

struct tree{
	count data;
	count l,r,ls,rs;
} shu[maxn<<5];

inline void add(count &k){
	if(!k){
		k=++all;
	}
}
inline void copy(count &k){
	shu[++all]=shu[k];
	k=all;
}
inline void pushup(count hao){
	shu[hao].data=(shu[shu[hao].ls].data+shu[shu[hao].rs].data);
}
void insert(count &hao,count l,count r,count pos){
	copy(hao);
	shu[hao].l=l,shu[hao].r=r;
	if(l==r){
		shu[hao].data++;
		return ;
	}
	count mid=(l+r)>>1;
	if(pos<=mid) insert(shu[hao].ls,l,mid,pos);
	else insert(shu[hao].rs,mid+1,r,pos);
	pushup(hao);
}
value ask(count hao1,count hao2,count k){
	if(shu[hao1].l==shu[hao1].r) return shu[hao1].l;
	count x=shu[shu[hao1].ls].data-shu[shu[hao2].ls].data;
	if(k<=x){
		return ask(shu[hao1].ls,shu[hao2].ls,k);
	}
	else{
		return ask(shu[hao1].rs,shu[hao2].rs,k-x);
	}
}

int main(){
	n=read(),m=read();
	for(count i=1;i<=n;i++){
		a[i]=read();
		book[i]=a[i];
	}
	std::sort(book+1,book+1+n);
	count cnt=std::unique(book+1,book+1+n)-book-1;
	
	for(count i=1;i<=n;i++){
		count zhen=std::lower_bound(book+1,book+1+cnt,a[i])-book;
		rt[i]=rt[i-1];
		insert(rt[i],1,n,zhen);
	}
	
	for(count i=1;i<=m;i++){
		count l=read(),r=read(),k=read();
		write(book[ask(rt[r],rt[l-1],k)]),putchar('\n');
	}
	return 0;
}

这个套路主要应用于值域和位置区间都有限制的情景,非常好算法,英雄联盟。

比如:P3923 美味

以后看到位运算的题一定要按位考虑

标签:count,ch,shu,hao,线段,value,BLOG1029,主席
From: https://www.cnblogs.com/eldenlord/p/17795713.html

相关文章

  • 2023 年 CCPC 网络预选赛 L.Partially Free Meal (主席树)
    传送门先插个图玩云顶之弈。#include<iostream>#include<cstring>#include<algorithm>#include<vector>#definelllonglong#definefsfirst#definesesecondconstlongdoubleeps=1e-9;constintN=2e5+10,M=2e5+10;constintM......
  • 主席树(更新)
    主席树学习(无详细讲解过程,因为思想很简单)目录主席树学习背景:可持久化线段树(主席树)模板:静态区间第k大更多应用:(其实就是加了一点其他模板)和树dfs一起出:一些总结:更新内容(2023/10/25)区间修改(持久化tag)背景:sensi:今天咱们做一下优化dp,你们看看这个简单题。https://www.luogu.c......
  • 主席树初步
    什么是主席树主席树即可持久化线段树这边其实我目前感觉就是支持查询历史版本的线段树原理每当线段树修改时,维护其过去的版本,将其复制下来(然后就MLE了改进:对集合的每一个版本维护一个单独的根,在修改数据时,只复制树的一部分(复制一张别人的图Orz)建树类似......
  • 主席树
    权值线段树思路:现将数值离散化每个节点存的是值在\(l\)~\(r\)之间的数的个数,用线段树维护作用:求\(k\)小值或\(k\)大值查某一数值的排名查询数组排序查前驱、后继求逆序对相比平衡树:码量小、简单P1801黑匣子离散化:sort(alls.begin(),alls.end());alls.eras......
  • 【主席树】P8201 [传智杯 #4 决赛] [yLOI2021] 生活在树上(hard version)题解
    P8201简单题。题中求的是\(dis_{a,t}\oplusdis_{t,b}=k\)是否存在,显然不好直接维护,考虑转化。令\(dist=dis_{a,t}\oplusdis_{t,b}\),\(val=\bigoplus\limits_{x\in\text{path}(a,b)}w_x\)。如果\(t\)在\(\text{path}(a,b)\)上,则\(dist=val\oplus......
  • 首位中国大陆学者!周志华当选新任国际人工智能联合会(IJCAI)理事会主席
    TOP前言“TOP大学来了”小编按,8月25日,在IJCAI2023闭幕式上,IJCAI执行委员会宣布欧洲科学院外籍院士、南京大学周志华教授当选为新一届的国际人工智能联合会理事会(IJCAITrustee)主席。“TOP大学来了”小编按,8月25日,在澳门举行的IJCAI2023闭幕式上,IJCAI执行委员会宣布欧洲科学院外......
  • 可持久化非确定状态AC自动分块维护线段平衡仙人掌优化最小费用最大流预处理混合图上莫
    P8946TheLostSymbol这种类型的dp的特点就是大部分转移形如\(f(i,j)\rightarrowf(i+1,j+1)\)之类的,并且当以上转移出现时原数组被清空,这就可以用一个deque来维护,然后对于全局赋值/全局加,需要对每个位置维护一个时间戳,并记录上一次赋值/加是什么时候,以便标记下传。(貌似......
  • Cousleur (ICPC 青岛) (值域主席树 + 逆序对 + multiset +mp)
    题目大意:给一个序列 n会有n次操作,每次都会删除一个数这个数是连续子序列里面最大的逆序对的个数^Q[i],q[i]给出思路:启发式拆分,每次选择长度小的序列来进行处理数学化:rev(逆序对个数)   rev(x+1,r)=rev(l,r)-rev(l,x-1)-(一个元素......
  • 【主席树】CF813 E. Army Creation
    【主席树】CF813E.ArmyCreation题目链接:https://codeforces.com/contest/813/problem/E题意多次询问,求一个区间内,所有数个数的总和,但相同的数最多被计算k次,强制在线。题解这道题和牛客一道题很像,是那道题的加强版,链接在这:题解链接。那道题可以离线,所以离线处理+树状数组......
  • 线段树+动态开点权值线段树+主席树学习笔记
    线段树一般用于维护符合结合律的信息。可以用于求区间最大值区间和区间最小值最大子段和甚至于最大负数最小正数之类的信息。事实上线段树只有你想不到,很少有做不到的,算是相当常用的数据结构。下面将结合个人理解和具体题目来讲一讲线段树。[https://www.luogu.com.cn/proble......