题意;
给一个数列${ a_i}$ 一些询问$(l_i,r_i)$ 问你$[l,r]$有多少个不同元素
题解:
其实本质思路和离线化处理询问的线段树/树状数组写法差不多,对区间$[x,r]$来说,所有数字只有$r$前面的最后一次出现才有意义
于是想到通过记录树的版本来保存情况
先保存数列,再对于每个数字依次建树,如果这个数字之前出现过,就把前一个树的该点删除,再插入新点,作为新树,否则正常键新树
#include <bits/stdc++.h> #define nd seg[now] #define ndp seg[pre] #define mid ((s+t)>>1) using namespace std; const int maxn=1e5+10; int casn,n,m,k; int num[maxn],rt[maxn],size,pos[maxn]; struct node{ int l,r,sum; }seg[maxn*20]; map<int,int>vis; void maketree(int s=1,int t=k,int &now=rt[0]){ now=++size;nd={s,t,0}; if(s==t) return ; maketree(s,mid,nd.l);maketree(mid+1,t,nd.r); } void update(int &now,int pre,int pos,int x,int s=1,int t=k){ now=++size; nd=ndp,nd.sum+=x; if(s==t)return ; if(pos<=mid)update(nd.l,ndp.l,pos,x,s,mid); else update(nd.r,ndp.r,pos,x,mid+1,t); } int query(int now,int l,int r,int s=1,int t=k){ if(l>t||r<s||l>r) return 0; if(l<=s&&r>=t) { return nd.sum; } return query(nd.l,l,r,s,mid)+query(nd.r,l,r,mid+1,t); } int main(){ scanf("%d",&k); for(int i=1;i<=k;i++){ scanf("%d",num+i); pos[i]=num[i]; } vis.clear(); size=0; maketree(); int id=0; for(int i=1;i<=k;i++){ if(vis[num[i]]!=0) { update(id,rt[i-1],vis[num[i]],-1); update(rt[i],id,i,1); }else update(rt[i],rt[i-1],i,1); vis[num[i]]=i; } scanf("%d",&m); while(m--){ int a,b; scanf("%d%d",&a,&b); printf("%d ",query(rt[b],a,k)); } return 0; }
Accepted
|
220 | 42 |
C++
|
2018-06-19 22:17:06
|