莫队详解

引子:

终于,今天学习了莫队。

这可是一种高级的暴力,区间问题神器。



题目:

来看一道裸题。

有n个数,m个询问,每个询问有一对数x、y,求x~y中有多少个不同的数。



思路1:

自然而然想到暴力。

时间复杂度:O(nm),令人窒息。

代码就不贴了(人人都会)



思路2:

线段树或树状数组搞一搞。

然而这不是重点(代码复杂度相对而言较高)



代码:

#include<cstdio>
#include<algorithm>
#define lowbit(x) ((x)&(-(x)))
using namespace std;
	int n,m;
	int b[500010],c[500010],last[1000010],ans[500010];
	struct node{int x,y,id;} a[200010];
bool cmp(node x,node y)
{
	return x.y==y.y?x.x<y.x:x.y<y.y;
}
void add(int x,int y)
{
	while(x<=n)
	{
		c[x]+=y;
		x+=lowbit(x);
	}
}
int getsum(int x)
{
	int sum=0;
	while(x)
	{
		sum+=c[x];
		x-=lowbit(x);
	}
	return sum;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&b[i]);
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d %d",&a[i].x,&a[i].y);
		a[i].id=i;
	}
	sort(a+1,a+m+1,cmp);
	int now=1;
	for(int i=1;i<=n;i++)
	{
		if(last[b[i]]) add(last[b[i]],-1);
		last[b[i]]=i;
		add(i,1);
		while(i==a[now].y&&now<=m)
		{
			ans[a[now].id]=getsum(a[now].y)-getsum(a[now].x-1);
			now++;
		}
		if(now==m+1) break;
	}
	for(int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
}


思路3:

重点来了!!!

我们可以优化一下思路1,就是暴力的升级版。

我们可以将这n个数分为块,每个块的长度为,这样就将一段数拆成了完整的块+不完整的块,那么我们只需要每次都对当前区间完整的块进行O(1)的询问就可以。

我们把同一个块中的元素按照右端点排序,不同的块则按照左端点排序,就可以保证有序,然后直接操作即可。



代码:

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
	int n,m,now;
	int p[500010],block[500010],hsh[500010],ans[500010];
	struct node{int x,y,id;} a[200010];
bool cmp(node x,node y)
{
	return block[x.x]==block[y.x]?x.y<y.y:x.x<y.x;
}
void init()
{
	int u=sqrt(n);
	for(int i=1;i<=n;i++)
		block[i]=(i-1)/u+1;
}
void move(int x,int d)
{
	if(d)
	{
		if(!hsh[p[x]]) now++;
		hsh[p[x]]++;
	}
	else
	{
		hsh[p[x]]--;
		if(!hsh[p[x]]) now--;
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&p[i]);
	scanf("%d",&m);
	init();
	for(int i=1;i<=m;i++)
	{
		scanf("%d %d",&a[i].x,&a[i].y);
		a[i].id=i;
	}
	sort(a+1,a+m+1,cmp);
	int l=0,r=0;
	for(int i=1;i<=m;i++)
	{
		while(l<a[i].x) move(l++,0);
		while(l>a[i].x) move(--l,1);
		while(r<a[i].y) move(++r,1);
		while(r>a[i].y) move(r--,0);
		ans[a[i].id]=now;
	}
	for(int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
}

阅读更多
个人分类: 莫队 树状数组
上一篇luogu P1196 [NOI2002]银河英雄传说
下一篇luogu P1801 黑匣子_NOI导刊2010提高(06)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭