【SPOJ3267】D-query-莫队算法

测试地址:D-query

题目大意:有N个数,Q个询问,每次询问某个区间[L,R]内有多少不同的数。

做法:这题我用主席树做过,题解请看这里

而这道题用莫队算法也能做,思考信息的转移:存储一个数组f表示每个数出现的次数,那么每次转移要么插入一个数,要么删除一个数,这样我们判断就很容易:当插入一个数时,如果原来这个数没有出现,那么答案+1;当删除一个数时,如果这个数原来就只剩一个了,那么答案-1。然后直接套莫队算法离线处理询问即可。

想法:这道题目用主席树写的话思想比较复杂(当然也有可能是更简单的方法我没想到),而用莫队算法就可以很优美的解决,所以说莫队算法对于大多数离线区间询问问题还是有很大的作用的。

以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int n,m,a[30010],f[1000010]={0};
struct query
{
  int l,r,id,ans;
  int pos;
}q[200010];

bool cmp1(query a,query b)
{
  return a.pos<b.pos||(a.pos==b.pos&&a.r<b.r);
}

bool cmp2(query a,query b)
{
  return a.id<b.id;
}

void init()
{
  scanf("%d",&n);
  for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
  scanf("%d",&m);
  int block=(int)sqrt((double)n+0.5);
  for(int i=1;i<=m;i++)
  {
    scanf("%d%d",&q[i].l,&q[i].r);
	q[i].id=i;
	q[i].pos=(q[i].l-1)/block;
  }
  sort(q+1,q+m+1,cmp1);
}

void transfer(int p,int &ans,int add)
{
  bool flag=0;
  if (f[a[p]]==0) flag=1;
  f[a[p]]+=add;
  if (flag&&f[a[p]]>0) ans++;
  else if (f[a[p]]==0) ans--;
}

void solve()
{
  int l=1,r=0,ans=0;
  for(int i=1;i<=m;i++)
  {
    if (r<q[i].r) while(r<q[i].r) r++,transfer(r,ans,1);
	if (q[i].l<l) while(q[i].l<l) l--,transfer(l,ans,1);
	if (r>q[i].r) while(r>q[i].r) transfer(r,ans,-1),r--;
	if (q[i].l>l) while(q[i].l>l) transfer(l,ans,-1),l++;
	q[i].ans=ans;
  }
  sort(q+1,q+m+1,cmp2);
  for(int i=1;i<=m;i++)
    printf("%d\n",q[i].ans);
}

int main()
{
  init();
  solve();
  
  return 0;
}


posted @ 2017-04-25 09:39  Maxwei_wzj  阅读(99)  评论(0编辑  收藏  举报