题目链接:http://www.spoj.com/problems/DQUERY/en/
题目大意:
给一段序列,每次询问l , r之间有多少个不同的数。
解题思路:
这道题以前做的时候用的是离线+线段树或者树状数组,最近学了主席树,就可以用主席树来在线解题了。而且貌似感觉自己用莫队解过这道题23333,反正解法很多,这里主要说主席树的解法,主席树的解法主要是记录每个数最右边的位置对答案的贡献。拿样例举例子,画个图应该很容易明白。
这个图呢就是对应样例画出来的对应1 1 2 1 3 的图,其实这里面每个1就是代表我这个位置对答案有1的贡献,而且只记录每个数字最后出现的位置,那么这样之后我们查询l r区间的时候 就直接在 root[r] l,n查询即可,
而且我感觉自己代码写的挺好看的2333
Ac代码:
#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int N=1e5+5;
const int maxn=1e6+10;
const int INF=1e9+7;
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
int n,m,cnt;
int a[N],root[N],last[maxn]; //last数组记录位置 因为数不大 不需要离散化
struct node
{
int l,r,sum;
}t[N*40];
void update(int l,int r,int &x,int y,int val,int pos) //主席树更新操作 val对应+1 -1
{
t[++cnt]=t[y],t[cnt].sum+=val,x=cnt;
if(l==r)
return ;
int m=(l+r)>>1;
if(pos<=m)
update(l,m,t[x].l,t[y].l,val,pos);
if(pos>m)
update(m+1,r,t[x].r,t[y].r,val,pos);
}
int query(int l,int r,int x, int L,int R) //主席树简单的查询操作
{
int ans=0;
if(L<=l&&r<=R)
return t[x].sum;
int m=(l+r)>>1;
if(L<=m)
ans+=(query(l,m,t[x].l,L,R));
if(R>m)
ans+=(query(m+1,r,t[x].r,L,R));
return ans;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
cnt=0;
memset(root,0,sizeof root);
memset(last,-1,sizeof last);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
if(last[a[i]]==-1)
update(1,n,root[i],root[i-1],1,i); //如果这个数第一次出现 将它所在位置贡献+1
else
{
int sk;
update(1,n,sk,root[i-1],1,i); //不是第一次出现 将上次出现的位置贡献消去
update(1,n,root[i],sk,-1,last[a[i]]); //当前位置贡献+1
}
last[a[i]]=i;
}
scanf("%d",&m);
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",query(1,n,root[r],l,n));
}
}
//system("pause");
}