[BZOJ1878] [SDOI2009] HH的项链 (树状数组)
Description
Input
Output
Sample Input
1 2 3 4 3 5
3
1 2
3 5
2 6
Sample Output
2
4
HINT
对于20%的数据,N ≤ 100,M ≤ 1000;
对于40%的数据,N ≤ 3000,M ≤ 200000;
对于100%的数据,N ≤ 50000,M ≤ 200000。
Source
Solution
挺经典的一道题。
大体思路是:对于每一个询问区间[l, r],我们只需关注[l, n]中第一次出现的颜色的位置,把答案+1
nxt[i]存储颜色a[i]的下一个位置在哪,把每一种颜色的第一次出现的位置的答案+1,举例如下:
a[i]: 1 4 4 2 3 4 3 3 1 2
ans[i]: 1 1 0 1 1 0 0 0 0 0
把询问操作按左端点排序,假如现在要执行询问[2, 4],那么ans[1]信息已失效,把a[i]的下一个对应位置更新
nxt[1] = 9
a[i]: 1 4 4 2 3 4 3 3 1 2
ans[i]: 1 1 0 1 1 0 0 0 1 0
答案就是1 + 0 + 1 = 2
再加入又要执行询问[6, 9],先更新[2, 5]的下一个的信息(因为a[1]已更新)
nxt[2] = 3, nxt[3] = 6, nxt[4] = 10, nxt[5] = 7:
a[i]: 1 4 4 2 3 4 3 3 1 2
ans[i]: 1 1 1 1 1 1 1 0 1 1
答案就是1 + 1 + 0 + 1 = 3,以此类推。
注意到这个方法可以保证每一种颜色在区间内只在第一次出现时被算过一遍。
复杂度是O(n^2),需用树状数组维护前缀和使复杂度降为O(nlogn)
1 #include <bits/stdc++.h> 2 using namespace std; 3 struct query 4 { 5 int id, l, r, ans; 6 }q[200005]; 7 int n, a[50005], fst[1000005], nxt[50005], BIT[50005], ctot; 8 9 bool cmp1(const query &lhs, const query &rhs) 10 { 11 return lhs.l == rhs.l ? lhs.r < rhs.r : lhs.l < rhs.l; 12 } 13 14 bool cmp2(const query &lhs, const query &rhs) 15 { 16 return lhs.id < rhs.id; 17 } 18 19 void update(int x, int val) 20 { 21 for(; x <= n; x += x & -x) 22 BIT[x] += val; 23 } 24 25 int query(int x) 26 { 27 int ans = 0; 28 for(; x; x -= x & -x) 29 ans += BIT[x]; 30 return ans; 31 } 32 33 int main() 34 { 35 int m, l = 0; 36 scanf("%d", &n); 37 for(int i = 1; i <= n; i++) 38 scanf("%d", a + i), ctot = max(ctot, a[i]); 39 for(int i = n; i; i--) 40 nxt[i] = fst[a[i]], fst[a[i]] = i; 41 for(int i = 0; i <= ctot; i++) 42 if(fst[i]) update(fst[i], 1); 43 scanf("%d", &m); 44 for(int i = 1; i <= m; i++) 45 scanf("%d%d", &q[i].l, &q[i].r), q[i].id = i; 46 sort(q + 1, q + m + 1, cmp1); 47 for(int i = 1; i <= m; i++) 48 { 49 while(l < q[i].l - 1) 50 if(nxt[++l]) update(nxt[l], 1); 51 q[i].ans = query(q[i].r) - query(q[i].l - 1); 52 } 53 sort(q + 1, q + m + 1, cmp2); 54 for(int i = 1; i <= m; i++) 55 printf("%d\n", q[i].ans); 56 return 0; 57 }