分析:
题目描述不说了,大意是,求一段区间内不同元素的种数。
看到区间,我们大概先想到的是暴力(然后炸掉)、线段树、树状数组、分块。
下面给出的是一种树状数组的想法。
首先,对于每一段区间里的数,如果出现重复的元素,我们只需要看最后一个就好了。所以,我们可以对所有需要查询区间的右端点进行从小到大的排序,从左往右枚举右端点维护一个从左向右的树状数组,表示一段区间的种类数。
听不懂的话我们举个栗子例子。
我们假设现在有一个序列:
now为现在的右端点;
insert(i , j)表示在第i 个位置出现了j个位重复的数,就需要我们在我们维护的树状数组序列中+j。
1 ,2 ,1 ,3
当now = 1时,insert(1 , 1),树状数组对应的序列就变成了1,0,0,0
当now = 2,insert(2,1),序列变成了1,1,0,0
当now = 3时,我们发现a[3]=1,而1之前已经出现过了,所以最后一次出现的地方(a[1])减1,即insert(1,−1),a[3]这个地方加1,即insert(3,1),序列变成了0,1,1,0
当now = 4时,insert(4 , 1),序列变成0,1,1,1
这样的话,我们枚举ii的时候处理一下就OK了.
最后whilewhile输出sum[ ](类似前缀和的一个数组如果查询区间[3~5],就变成sum[5]−sum[2])。
你会发现代码中还有一个last[ ],last[i]根据我的解释应该很好懂吧,表示的就是ii这个元素最后出现的位置。。
好了,代码奉上。。
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; const int maxn = 1000005; inline int read(){ char ch = getchar(); int f = 1 ,x = 0; while(ch > '9' || ch < '0'){if(ch == '-')f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + ch - '0';ch = getchar();} return x * f; } int n,a[maxn],m; struct Edge{ int L,R,val; }e[maxn << 2]; int last[maxn],sum[maxn],ans[maxn]; int tmp; bool cmp(Edge a,Edge b){return a.R < b.R;} int lowbit(int x){return x & (-x);} void insert(int x,int v){ for(int i=x;i<=n;i+=lowbit(i)) sum[i] += v; } int query(int x){ int s = 0; for(int i=x;i;i-=lowbit(i)) s += sum[i]; return s; } int main(){ n = read(); for(int i=1;i<=n;i++) a[i] = read(); m = read(); for(int i=1;i<=m;i++){ e[i].L = read(); e[i].R = read(); e[i].val = i; } sort(e + 1 , e + 1 + m , cmp); int now = 1; for(int i=1;i<=n;i++){ insert(i , 1); if(last[a[i]]) insert(last[a[i]] , -1); last[a[i]] = i; tmp = query(i); while(e[now].R == i && now <= m){ ans[e[now].val] = tmp - query(e[now].L - 1); now++; } } for(int i=1;i<=m;i++) printf("%d ",ans[i]); return 0; }