题目链接:https://vjudge.net/problem/SPOJ-DQUERY
题意:给定数字序列,求任意区间内的不同数字的个数
解法:用树状数组维护 1 ~ i 的区间内不同数字个数的前缀和,首要解决的问题就是同一区间内相同数字统计时相互影响的问题,解决方法如下:离线存储查询的区间,对查询区间按照左端点排序,这样可以优先处理左边的 元素;然后预处理出每一元素下一跳到相同元素的 nxt[]; 这样随着坐标轴指针 l 的移动,l 左边的元素将不会产生任何影响,因为所产生的影响会在前缀和的做差运算中抵消;只需要对 l 指针左区间的元素的下一跳的位 置进行 add + 1 操作,从而更新当前查询区间的信息。
BIT代码实现:
1 //190ms 19.5MB 2 #include <bits/stdc++.h> 3 using namespace std; 4 5 const int N = 30010; 6 const int M = 200010; 7 int a[N], bit[N]; 8 9 void init() { 10 memset(bit, 0, sizeof(bit)); 11 } 12 13 int lowbit(int x) { 14 return x & -x; 15 } 16 17 void add(int x, int val) { 18 while(x < N) { 19 bit[x] += val; 20 x += lowbit(x); 21 } 22 } 23 24 int sum(int x) { 25 int ret = 0; 26 while(x > 0) { 27 ret += bit[x]; 28 x -= lowbit(x); 29 } 30 return ret; 31 } 32 33 struct Query{ 34 int l, r, p; 35 bool operator < (const Query& p)const{ 36 return l < p.l; 37 } 38 } query[M]; 39 int n, q; 40 map<int, int> mp; 41 int nxt[N], ans[M]; 42 43 int main() 44 { 45 scanf("%d", &n); 46 mp.clear(); 47 init(); 48 for (int i = 1; i <= n; ++ i) { 49 scanf("%d", &a[i]); 50 if(mp.find(a[i]) == mp.end()) { 51 mp[a[i]] = i; 52 add(i, 1); 53 } 54 } 55 mp.clear(); 56 for (int i = n; i >= 1; -- i) { 57 if(mp.find(a[i]) == mp.end()) nxt[i] = n + 1; 58 else nxt[i] = mp[a[i]]; 59 mp[a[i]] = i; 60 } 61 scanf("%d", &q); 62 for (int i = 1; i <= q; ++ i) { 63 scanf("%d%d", &query[i].l, &query[i].r); 64 query[i].p = i; 65 } 66 sort(query+1, query+1+q); 67 int pp = 1; 68 for (int i = 1; i <= q; ++ i) { 69 while(pp<=n && pp < query[i].l) add(nxt[pp++], 1); 70 ans[query[i].p] = sum(query[i].r) - sum(query[i].l-1); 71 } 72 for (int i = 1; i <= q; ++ i) printf("%d\n", ans[i]); 73 return 0; 74 }
时间: 2024-10-03 14:06:11