【SPOJ3267】D-query-主席树应用

测试地址:D-query

题目大意:给一个长为N的数列,Q次询问,每次询问一个区间[i,j]内有多少个不同的数。

做法:用主席树解决问题,将数列上的数字逐个插入,对于第i次操作,在第i个位置+1并生成一个新的版本,表示在第i个位置新出现一个数,然而要排除重复数字的干扰,我们就把下一个出现这个数的位置称为next[i],在第i个版本中的第next[i]个位置-1,然后对于每一个询问[i,j],答案就是第j个版本与第i-1个版本相减后区间[1,j]的和。

Update:这题后来我用莫队算法也做了,题解请看这里

以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,q,tot=0,last[1000010]={0},nxt[100010]={0},rt[100010]={0};
struct segnode
{
  int lc,rc,sum;
}seg[3000010];

void buildtree(int &no,int l,int r)
{
  no=++tot;
  seg[no].lc=seg[no].rc=seg[no].sum=0;
  if (l==r) return;
  int mid=(l+r)>>1;
  buildtree(seg[no].lc,l,mid);
  buildtree(seg[no].rc,mid+1,r);
}

void modify(int last,int &no,int l,int r,int pos,int val)
{
  no=++tot;
  seg[no]=seg[last];
  if (l==r)
  {
    seg[no].sum+=val;
	return;
  }
  int mid=(l+r)>>1;
  if (pos<=mid) modify(seg[last].lc,seg[no].lc,l,mid,pos,val);
  else modify(seg[last].rc,seg[no].rc,mid+1,r,pos,val);
  seg[no].sum=seg[seg[no].lc].sum+seg[seg[no].rc].sum;
}

int query(int fnt,int lst,int l,int r,int t)
{
  if (r<=t) return seg[lst].sum-seg[fnt].sum;
  int tot=0,mid=(l+r)>>1;
  tot+=query(seg[fnt].lc,seg[lst].lc,l,mid,t);
  if (t>mid) tot+=query(seg[fnt].rc,seg[lst].rc,mid+1,r,t);
  return tot;
}

int main()
{
  scanf("%d",&n);
  buildtree(rt[0],1,n);
  
  for(int i=1,a;i<=n;i++)
  {
    scanf("%d",&a);
	last[a]=nxt[last[a]]=i;
  }
  
  for(int i=1;i<=n;i++)
  {
    modify(rt[i-1],rt[i],1,n,i,1);
	if (nxt[i]) modify(rt[i],rt[i],1,n,nxt[i],-1);
  }
  
  scanf("%d",&q);
  for(int i=1,a,b;i<=q;i++)
  {
    scanf("%d%d",&a,&b);
	printf("%d
",query(rt[a-1],rt[b],1,n,b));
  }
  
  return 0;
}


原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793783.html