SP3267 DQUERY - D-query(莫队/主席树)

本文详细介绍了莫队算法和主席树的实现原理及应用。莫队算法通过预处理和分块技术优化区间查询问题,主席树则利用线段树变种高效处理区间更新与查询。代码示例清晰展示了两种算法的结构与操作流程。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

讲解:https://www.cnblogs.com/WAMonster/p/10118934.html

1.莫队

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 1010000
#define maxb 1010
using namespace std;
int aa[maxn],cnt[maxn],belong[maxn];
int n,m,size,bnum,now,ans[maxn];
struct query{
	int l,r,id;
}q[maxn];
int cmp(query a,query b)
{
	return (belong[a.l]^belong[b.l])?belong[a.l]<belong[b.l]:(belong[a.l]&1)?a.r<b.r:a.r>b.r;
}
void add(int pos)
{
	if(!cnt[aa[pos]]) ++now;
	++cnt[aa[pos]];
}
void del(int pos)
{
	--cnt[aa[pos]];
	if(!cnt[aa[pos]]) --now;
}
int main()
{
	scanf("%d",&n);
	size=sqrt(n);
	bnum=n/size;if(n%size) bnum++;
	for(int i=1;i<=n;i++)
	{
		belong[i]=(i-1)/size+1;
	}
	for(int i=1;i<=n;i++) scanf("%d",&aa[i]);
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&q[i].l,&q[i].r);
		q[i].id=i;
	}
	sort(q+1,q+m+1,cmp);
	int l=1,r=0;
	for(int i=1;i<=m;i++)
	{
		int ql=q[i].l,qr=q[i].r;
		while(l<ql)  del(l++);
		while(l>ql)  add(--l);
		while(r<qr)  add(++r);
		while(r>qr)  del(r--);
		ans[q[i].id]=now;
	}
	for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}

2.主席树
思路:root[i]维护的是数组[1,i]中每个数的最后位置
这样查询 [ l , r ] 内的答案就是查询第 r 个版本的主席树中 [ l , r ] 的 sum
代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N=2e5+10;
#define ll long long
int n,m,x,p;
struct tree{
	int l,r;int cnt;
}t[N*4+N*17];
int root[N],idx;
int build(int l,int r)
{
	int q=++idx;
	t[q].cnt=0;
	if(l==r) return l;
	int mid=(l+r)/2;
	t[q].l=build(l,mid);
	t[q].r=build(mid+1,r);
	return q;
}
int insert(int p,int l,int r,int x,int k)
{
	
	int q=++idx;
	t[q]=t[p];
	if(l==r) 
	{
		t[q].cnt+=k;
		return q;
	}
	int mid=(l+r)/2;
	if(x<=mid)
	{
		t[q].l=insert(t[p].l,l,mid,x,k);
	}
	else t[q].r=insert(t[p].r,mid+1,r,x,k);
	t[q].cnt=t[t[q].l].cnt+t[t[q].r].cnt;
	return q;
}
int query(int p,int l,int r,int L,int R)
{   
	if(l>=L&&r<=R) return t[p].cnt;	
	int mid=(l+r)/2;
	int ans=0;
	if(L<=mid)
	{
		ans=ans+query(t[p].l,l,mid,L,R);
	}
	if(R>mid) 
	    ans=ans+query(t[p].r,mid+1,r,L,R);
	return ans; 
}
int last[1000010];
int main()
{
	scanf("%d",&n);
	root[0]=build(1,n);
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		if(last[x]==0)
		{
			root[i]=insert(root[i-1],1,n,i,1);
		}
		else
		{
			root[i]=insert(root[i-1],1,n,last[x],-1);
			root[i]=insert(root[i],1,n,i,1);
		}
		last[x]=i;
	}
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		printf("%d\n",query(root[r],1,n,l,r));
	}

	
}

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值