Mex函数(可持久化线段树)

Posted jpphy0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mex函数(可持久化线段树)相关的知识,希望对你有一定的参考价值。

问题

  • 长度为n的自然数序列,求任意区间内未出现过的最小自然数。长度n、询问次数m、自然数 a i a_i ai满足:
    • 1 ≤ n , m ≤ 200000 1\\leq n,m\\leq 200000 1nm200000
    • 0 ≤ a i ≤ 1000000000 0 \\leq a_i \\leq 1000 000 000 0ai1000000000

分析

  • → \\rightarrow 时间
  • 各时刻的线段树维护区间内的各数最后一次出现的位置的最小值
  • 增量持久化策略优化线段树
  • 大于n的 a i a_i ai 按n+1处理

代码

#include<bits/stdc++.h>
using namespace std;
const int MXN = 2e5+5;
int N, M, tot, ver[MXN];
struct TreeNode{
	int l, r; // 左右子树
	int p; // 各数的最后位置的最小值
}tree[MXN<<5];
void pushup(int root){
	TreeNode &rt = tree[root];
	TreeNode &ls = tree[rt.l], &rs = tree[rt.r];
	rt.p = min(ls.p, rs.p);
}
int build(int l, int r){
	int root = ++tot;
	TreeNode &rt = tree[root];
	rt.p = 0, rt.l = 0, rt.r = 0;
	return root;
}
int change(int node, int l, int r, int x, int p){ // x在p位置上出现
	int root = ++tot;
	TreeNode &rt = tree[root];
	rt = tree[node];
	if(l == r) {rt.p = p; return root;}
	int mid = (l+r)>>1;
	if(x <= mid) rt.l = change(rt.l, l, mid, x, p);
	else rt.r = change(rt.r, mid+1, r, x, p);
	pushup(root);
	return root;
}
int query(int node, int L, int R, int l, int r){
	if(l == r) return l;
	TreeNode &rt = tree[node];
	TreeNode &ls = tree[rt.l], &rs = tree[rt.r];
	int mid = (l+r)>>1;
	if(ls.p < L) return query(rt.l, L, R,  l, mid);
	else if(rs.p < L) return query(rt.r, L, R, mid+1, r);
	else return R;
}
int main(){
	int t, x, L, R;
	scanf("%d", &t);	
	while(t--){
		tot = 0;
		scanf("%d%d", &N, &M);
		ver[0] = build(0, N+1);
		for(int i = 1; i <= N; ++i){
			scanf("%d", &x);
			if(x > N) x = N+1;
			ver[i] = change(ver[i-1], 0, N+1, x, i);
		}
		while(M--){
			scanf("%d%d", &L, &R);
			printf("%d\\n", query(ver[R], L, R, 0, N+1));
		}
	}
    return 0;
}

以上是关于Mex函数(可持久化线段树)的主要内容,如果未能解决你的问题,请参考以下文章

Rmp(Mex) & Destiny & 楼房重建(线段树上二分)

可持久化线段树/主席树(静态)

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

可持久化线段树--主席树

可持久化线段树

p3834 可持久化线段树模板(主席树)