求区间 mex

莫队+分块

值域分块,每个块处理有多少个书出现过。

#include <bits/stdc++.h>
using namespace std;
inline void read(int& x)
{
	x=0;
	int f=1;
	char c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-')f=-1;
		c=getchar();
	}
	while('0'<=c&&c<='9')
	{
		x=(x<<1)+(x<<3)+c-'0';
		c=getchar();
	}
	x*=f;
}
const int N=2e5+5,B=448;
int n,m,a[N],id[N],block,cnt[N],num[N];
int ans[N];
struct node 
{
	int l,r,id;
}q[N];
bool cmp(node u,node v)
{
	if(id[u.l]==id[v.l])return u.r<v.r;
	return id[u.l]<id[v.l];
}
void del(int x)
{
	if(cnt[x]==1)num[x/B]--;
	cnt[x]--;
}
void add(int x)
{
	if(cnt[x]==0)num[x/B]++;
	cnt[x]++;
}
void query(int x)
{
	for(int i=1;i<=B;i++)
        if(num[i-1]!=B)for(int j=(i-1)*B;j<i*B;j++)if(!cnt[j])ans[x]=j;return;}
}
int main()
{
	read(n),read(m);
	block=sqrt(n);
	for(int i=1;i<=n;i++)
	{
		read(a[i]);
		id[i]=(i-1)/block+1;
	}
	for(int i=1;i<=m;i++)
	{
		read(q[i].l),read(q[i].r);
		q[i].id=i;
	}
	sort(q+1,q+m+1,cmp);
	int l=q[1].l,r=q[1].r;
	for(int i=l;i<=r;i++)add(a[i]);
	query(q[1].id);
	for(int i=2;i<=m;i++)
	{
		while(l<q[i].l)del(a[l++]);
		while(l>q[i].l)add(a[--l]);
		while(r<q[i].r)add(a[++r]);
		while(r>q[i].r)del(a[r--]);
		query(q[i].id);
	}
	for(int i=1;i<=m;i++)
	{
		cout<<ans[i]<<'\n';
	}
	return 0;
}

可持久化线段树

维护每个数在第 i 个历史前,最后一次出现的位置

对于查询 [l,r],在第 r 个历史版本中找到 最后一次在第 l 个历史版本最小值。

#include <bits/stdc++.h>
using namespace std;
const int N=3e5+5,mx=3e5;
int n,m,b[N];

struct node
{
	int val,ls,rs;
}t[N*40];
int idx=0,rt[N];

namespace tree
{

inline void upd(int &p,int pre,int l,int r,int x,int k)
{
	p=++idx;
	t[p]=t[pre];
	if(l==r) { t[p].val=k; return; }
	int mid=(l+r)/2;
	if(x<=mid)upd(t[p].ls,t[pre].ls,l,mid,x,k);
	else upd(t[p].rs,t[pre].rs,mid+1,r,x,k);
	t[p].val=min(t[t[p].ls].val,t[t[p].rs].val);
}

inline int query(int p,int l,int r,int k)
{
	if(l==r)return l;
	int mid=(l+r)/2;
	if(t[t[p].ls].val<k)return query(t[p].ls,l,mid,k);//如果左子树的值域中,有最近一次出现的时间<当前查询的k时刻的点,就确定是左子树
	else return query(t[p].rs,mid+1,r,k);//否之,去右子树
}

inline int mex(int l,int r)
{
	if(l>r)return 0;
	return query(rt[r],0,n,l);//查询
}

}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>b[i];
		tree::upd(rt[i],rt[i-1],0,n,b[i],i);
	}
	while(m--)
	{
		int st,en;
		cin>>st>>en;
		cout<<tree::mex(st,en)<<'\n';
	}
	return 0;
}
posted @   tyccyt  阅读(4)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示