spoj 3267. D-query 主席树求区间不同数的个数

Given a sequence of n numbers a1, a2, ..., an and a number of d-queries. A d-query is a pair (i, j) (1 ≤ i ≤ j ≤ n). For each d-query (i, j), you have to return the number of distinct elements in the subsequence ai, ai+1, ..., aj.

Input

  • Line 1: n (1 ≤ n ≤ 30000).
  • Line 2: n numbers a1, a2, ..., an (1 ≤ ai ≤ 106).
  • Line 3: q (1 ≤ q ≤ 200000), the number of d-queries.
  • In the next q lines, each line contains 2 numbers i, j representing a d-query (1 ≤ i ≤ j ≤ n).

Output

  • For each d-query (i, j), print the number of distinct elements in the subsequence ai, ai+1, ..., aj in a single line.

Example

Input
5
1 1 2 1 3
3
1 5
2 4
3 5

Output
3
2
3

/*
求区间内不重复的数的个数。
扫描数列建立可持久化线段树,第i个数若第一次出现,则在线段树中的位置i加1;
若不是第一次出现,将上次出现的位置减1,在本次位置加1。
对于每个询问的区间 [L,R],在第R个版本上的线段树只有前R个数,在线段树上查询位置L,
对经过的区间中的和进行累计即可*/
#include<stdio.h>
#include<map>
#define M 1000000
#define N 35000
using namespace std;
map<int ,int >ma;
int n,num,c[M],lson[M],rson[M],a[N],t[N];
int build(int x,int y)
{
   int root=num++;
   c[root]=0;
   if(x!=y)
   {
      int mid=(x+y)>>1;
      lson[root]=build(x,mid);
      rson[root]=build(mid+1,y);
   }
   return root;
}
int insert(int root,int k,int val)
{
  int newroot,tp,l,r;
  newroot=num++; tp=num-1;
   c[newroot]=c[root]+val;
   l=1;r=n;
   while(l<r)
   {
     int mid=(l+r)>>1;
     if(k<=mid)
     {
        lson[newroot]=num++; rson[newroot]=rson[root];
        newroot=lson[newroot];  root=lson[root];
        r=mid;
     }
     else
     {
         rson[newroot]=num++;  lson[newroot]=lson[root];
         newroot=rson[newroot];  root=rson[root];
         l=mid+1;
     }
     c[newroot]=c[root]+val;
   }
   return tp;
}
int query(int x,int y,int root,int k)
{
   int mid=(x+y)>>1;
   if(x==y)  return c[root];
   if(k<=mid)
       return c[rson[root]]+query(x,mid,lson[root],k);
   else
      return query(mid+1,y,rson[root],k);
}
int main()
{
  int i,m,x,y,tmp;
  //freopen("a.txt","r",stdin);
  while(scanf("%d",&n)!=EOF)
  {
     for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
     scanf("%d",&m);  num=0;
     t[0]=build(1,n);
     ma.clear();
     for(i=1;i<=n;i++)
     {
         if(ma.find(a[i])==ma.end())
            t[i]=insert(t[i-1],i,1);
         else
         {
             tmp=insert(t[i-1],ma[a[i]],-1);
             t[i]=insert(tmp,i,1);
         }
         ma[a[i]]=i;
     }
     for(i=1;i<=m;i++)
     {
        scanf("%d%d",&x,&y);
        printf("%d\n",query(1,n,t[y],x));
     }
  }
  return 0;
}


 树状数组做法:
/*做法:将查询按右端点从小到大排序。从左到右扫,如果该数之前出现过,
则将之前出现过的位置相应删除;当前位置则添加1。这样做就保证每次扫描
到的某一位置,以该位置为右端点的区间都相应地确定了。*/
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<map>
#define N 35000
#define M 250000
using namespace std;
struct node
{
  int x,y,id;
}b[M];
int c[N],ans[M],a[N];
map<int ,int >ma;
bool cmp(node a,node b)
{
  return a.y<b.y;
}
int lowbit(int x)
{
  return x&(-x);
}
void update(int x,int val)
{
   while(x<=N)
   {
     c[x]+=val;
     x+=lowbit(x);
   }
}
int sum(int x)
{
  int s=0;
  while(x)
  {
   s+=c[x];
   x-=lowbit(x);
  }
  return s;
}
int main()
{
   int n,i,m,pre,j;
   //freopen("a.txt","r",stdin);
   while(scanf("%d",&n)!=EOF)
   {
     for(i=1;i<=n;i++)
       scanf("%d",&a[i]);
      scanf("%d",&m);
      for(i=0;i<m;i++)
      {
        scanf("%d%d",&b[i].x,&b[i].y);
        b[i].id=i;
      }
      sort(b,b+m,cmp);  pre=1;
      memset(c,0,sizeof(c));
      for(i=0;i<m;i++)
      {
        for(j=pre;j<=b[i].y;j++)
        {
           if(ma.find(a[j])==ma.end())
                update(j,1);
            else
            {
               update(ma[a[j]],-1);
               update(j,1);
            }
            ma[a[j]]=j;
        }
        ans[b[i].id]=sum(b[i].y)-sum(b[i].x-1);
        pre=b[i].y+1;
      }
      for(i=0;i<m;i++)
        printf("%d\n",ans[i]);
   }
   return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值