主席树(可持续化线段树)板子

主席树是什么

一般来说,主席树是n个权值线段树组合成的一棵杂树。其中n是数组大小,举个例子:

数组 [2,3,1,4],由它构成的主席树就是[], [2], [2,3], [2,3,1], [2,3,1,4]对应的五棵权值线段树一起建出来的一棵杂树。下面讲几个重点问题:

  1. 如何理解权值线段树:我的理解是,给一个桶建树,就是权值线段树。不过要注意这个”桶“常常需要离散化。
  2. 主席树如何实现省空间和省时间:主席树中,只有空数组对应的0号树是完整的,后面的树都是在前面树的基础上加一条链组成树,重复利用了之前的信息。

主席树板子

板子题:【模板】可持久化线段树 2 - 洛谷 

#include<bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); i++)
#define ROF(i, a, b) for (int i = (a); i >= (b); i--) 
#define int long long
const int N = 2e5+10;
int n,m,a[N];
vector<int> v;
int rt[N],tot=0;
struct PT{
    int ls,rs,num; //左儿子,右儿子,数字数量
}t[N<<6];

int find(int x){return lower_bound(v.begin(),v.end(),x)-v.begin()+1;} //根据val找pos
void ins(int pre,int&u,int l,int r,int pos){
	u=++tot; //每次insert都要新建结点
	t[u]=t[pre]; //u继承pre的信息
	t[u].num++; //数字数量+1
	if(l==r) return;
	int mid=l+r>>1; //二分区间,准备向下处理两个儿子
	if(pos<=mid) ins(t[pre].ls, t[u].ls, l, mid, pos);
	else ins(t[pre].rs, t[u].rs, mid+1, r, pos);
}
int query(int L,int R, int l,int r,int k){
	if(l>=r) return l; //区间长度只剩1,说明找到了
	int mid=l+r>>1, Ldiff=t[t[R].ls].num-t[t[L].ls].num; //中间位置,左儿子数字数量差
	if(Ldiff>=k) return query(t[L].ls, t[R].ls, l, mid, k);
	else return query(t[L].rs, t[R].rs, mid+1, r, k-Ldiff);
}
inline void solve(){
	cin>>n>>m;
	FOR(i,1,n) cin>>a[i], v.push_back(a[i]);
	sort(v.begin(), v.end()); v.erase(unique(v.begin(),v.end()),v.end()); //排序+去重
	int D=v.size(); //a数组中只出现了D种数字

	FOR(i,1,n){
		rt[i]=rt[i-1]; //继承之前的信息
		int pos = find(a[i]);
		ins(rt[i], rt[i], 1, D, pos); //让pos位置+1
	}
	FOR(i,1,m){
		int l,r,k; cin>>l>>r>>k; //输入[l,r],询问其中第k大
		cout<<v[query(rt[l-1], rt[r], 1, D, k)-1]<<'\n'; //输出结果(注意find中加了1,这里对应位置要-1)
	}
}
signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int T=1; //cin>>T;
	while(T--) solve();
}

以下是练习题:

1.Two permutations - 洛谷

题意翻译

  • 给定两个长度均为 n 的 排列
  • m 次询问。每次询问您要求出在第一个排列的 [l1, r1] 和第二个排列的 [l2, r2] 同时出现的数有多少个。
  • 1<=n<=1e6,1<=m<=2e5。强制在线。(原题中有写到转换关系,这里要看原题)

代码: 

#include<bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); i++)
#define ROF(i, a, b) for (int i = (a); i >= (b); i--) 
// #define int long long
const int N = 1e6+10;

int n,m,pos[N];
int rt[N],tot=0;
int ans=0;
struct PT{
    int ls,rs,num; //左儿子,右儿子,数字数量
}t[N*40];

int rd(){
    int x = 1;
    char ch;
    while (ch = getchar(),ch < '0' || ch > '9') 
		if (ch == '-') 
			x = -1;
    int s = ch-'0';
    while (ch = getchar(),ch >= '0' && ch <= '9') 
		s = s * 10 + ch - '0';
    return s * x;
}
int f(int x){return (x-1+ans)%n+1;}
void ins(int pre,int&u,int l,int r,int pos){
	u=++tot; //每次insert都要新建结点
	t[u]=t[pre]; //u继承pre的信息
	t[u].num++; //数字数量+1
	if(l==r) return;
	int mid=l+r>>1; //二分区间,准备向下处理两个儿子
	if(pos<=mid) ins(t[pre].ls, t[u].ls, l, mid, pos);
	else ins(t[pre].rs, t[u].rs, mid+1, r, pos);
}
int query(int o,int l,int r,int x,int y){
	if(x<=l && r<=y) return t[o].num;
	int mid=l+r>>1, res=0;
	if(x<=mid) res+=query(t[o].ls,l,mid,x,y);
	if(y >mid) res+=query(t[o].rs,mid+1,r,x,y);
	return res;
}
signed main(){
	cin>>n;
	FOR(i,1,n) pos[rd()]=i;
	FOR(i,1,n) ins(rt[i-1],rt[i],1,n,pos[rd()]);
	cin>>m;
	while(m--){
		int w=f(rd()), x=f(rd()), y=f(rd()), z=f(rd());
		if(w>x) swap(w,x);
		if(y>z) swap(y,z);
		ans = query(rt[z],1,n,w,x)-query(rt[y-1],1,n,w,x);
		printf("%d\n",ans);
		ans++;
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值