对于该题,离线的做法是树状数组或者线段树。
如果强制在线的话,可以用主席树做到O(mlogn)。
考虑到这样一个性质,对于询问[l,r]出现的数字种数。其答案就是to[i]>r的数字数。 其中to[i]表示的是第i个数的下一个相同的数出现的下标,没有则=n+1.
很幸运这个性质是满足区间减法的,也就是说对于[1,r]和[1,l-1]的to[i]域,是可以相减得到[l,r]的to[i]域的。
于是我们可以用主席树来解决这个问题。
对于一组询问,实际上就是求[l-1,r]这颗线段树上的区间[r+1,n+1]的出现次数总和。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi 3.1415926535 # define eps 1e-9 # define MOD 1000000009 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int res=0, flag=0; char ch; if((ch=getchar())=='-') flag=1; else if(ch>='0'&&ch<='9') res=ch-'0'; while((ch=getchar())>='0'&&ch<='9') res=res*10+(ch-'0'); return flag?-res:res; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=50005; //Code begin... int root[N], s[N*80], ls[N*80], rs[N*80], sz, vis[1000005], to[N]; void insert(int l, int r, int x, int &y, int val){ y=++sz; s[y]=s[x]+1; if (l==r) return ; ls[y]=ls[x]; rs[y]=rs[x]; int mid=(l+r)>>1; if (val<=mid) insert(l,mid,ls[x],ls[y],val); else insert(mid+1,r,rs[x],rs[y],val); } int query(int l, int r, int x, int y, int L){ if (r<L) return 0; if (l>=L) return s[y]-s[x]; int mid=(l+r)>>1; return query(l,mid,ls[x],ls[y],L)+query(mid+1,r,rs[x],rs[y],L); } int main() { int n, m, l, r, x; scanf("%d",&n); FOR(i,1,n) { scanf("%d",&x); if (vis[x]) to[vis[x]]=i; vis[x]=i; } FOR(i,1,n) if (to[i]==0) to[i]=n+1; FOR(i,1,n) insert(1,n+1,root[i-1],root[i],to[i]); scanf("%d",&m); FOR(i,1,m) { scanf("%d%d",&l,&r); printf("%d\n",query(1,n+1,root[l-1],root[r],r+1)); } return 0; }