牛客网多校训练第一场 J - Different Integers(树状数组 + 问题转换)
链接:
https://www.nowcoder.com/acm/contest/139/J
题意:
给出n个整数的序列a(1≤ai≤n)和q个询问(1≤n,q≤1e5),每个询问包含两个整数L和R(1≤L,R≤n)。
对于每个询问,输出a[1...L]和a[R...n]的不同数字的个数。
分析:
将原数组复制一份拼接到末尾,把询问a[1...L]和a[R...n]转换为询问a[R...L+n]。
设kind[i]为a[1...i]出现的数字种类,
则询问a[L...R]的答案为 kind[R] - kind[L-1] +(a[1...L-1]和a[L...R]同时出现的数字种类)。
可以用树状数组维护a[1...L-1]和a[L...R]同时出现的数字种类。
如果a[i]在区间1...L-1出现过,则对应的树状数组位置的值应为1。查询时只需查询区间L...R。
可以对询问区间按左端点排序,左端点每次右移时把对应的数的下一个位置在树状数组里加1即可。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 const int MAXS = 1e6 + 5; 7 int sf[MAXS]; 8 void add(int p, int v, int n) { 9 while(p <= n) sf[p] += v, p += p&-p; 10 } 11 int sum(int p) { 12 int res = 0; 13 while(p) res += sf[p], p -= p&-p; 14 return res; 15 } 16 17 struct REGION { 18 int L, R, id; 19 bool operator < (const REGION& that) const { 20 return L < that.L; 21 } 22 } r[MAXS]; 23 int a[MAXS], kind[MAXS], nxt[MAXS], rec[MAXS], ans[MAXS]; 24 25 int main() { 26 int n, q; 27 while(~scanf("%d%d", &n, &q)) { 28 for(int i = 1; i <= n; i++) scanf("%d", &a[i]); 29 for(int i = 0; i < q; i++) { 30 scanf("%d%d", &r[i].R, &r[i].L); 31 r[i].R += n; 32 r[i].id = i; 33 } 34 memcpy(a+n+1, a+1, n*sizeof(int)); 35 n *= 2; 36 memset(rec, false, sizeof(rec)); 37 for(int i = 1; i <= n; i++) { 38 if(rec[a[i]]) kind[i] = kind[i-1]; 39 else kind[i] = kind[i-1] + 1, rec[a[i]] = true; 40 } 41 memset(rec, false, sizeof(rec)); 42 for(int i = n; i > 0; i--) { 43 nxt[i] = rec[a[i]]; 44 rec[a[i]] = i; 45 } 46 sort(r, r + q); 47 memset(sf, 0, sizeof(sf)); 48 for(int p = 1, i = 0; i < q; i++) { 49 while(p < r[i].L) add(nxt[p], 1, n), p++; 50 ans[r[i].id] = kind[r[i].R]-kind[r[i].L-1] + sum(r[i].R)-sum(r[i].L-1); 51 } 52 for(int i = 0; i < q; i++) printf("%d\n", ans[i]); 53 } 54 return 0; 55 }