[luoguP1972] [SDOI2009]HH的项链(莫队 || 树状数组 || 主席树)
莫队基础题,适合我这种初学者。
莫队是离线算法,通常不带修改,时间复杂度为 O(n√n)
我们要先保证通过 [ l , r ] 求得 [ l , r + 1 ] , [ l , r - 1 ] , [ l - 1 , r ] , [ l + 1 , r ] 的效率是O(1)的
对于莫队的理解,移步远航休息栈
——代码
1 #include <cmath> 2 #include <cstdio> 3 #include <iostream> 4 #include <algorithm> 5 6 int n, m, S, ans = 1; 7 int a[50001], ton[1000001], anslist[200001]; 8 struct node 9 { 10 int l, r, id, num; 11 }q[200001]; 12 13 inline int read() 14 { 15 int x = 0; 16 char ch = getchar(); 17 for(; !isdigit(ch); ch = getchar()); 18 for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0'; 19 return x; 20 } 21 22 inline bool cmp(node x, node y) 23 { 24 return x.id ^ y.id ? x.id < y.id : x.r < y.r; 25 } 26 27 int main() 28 { 29 int i, x, y; 30 n = read(); 31 for(i = 1; i <= n; i++) a[i] = read(); 32 m = read(); 33 for(i = 1; i <= m; i++) q[i].l = read(), q[i].r = read(), q[i].num = i; 34 S = sqrt(n); 35 for(i = 1; i <= m; i++) q[i].id = q[i].l / S + 1; 36 std::sort(q + 1, q + m + 1, cmp); 37 x = q[1].l, y = q[1].l; 38 ton[a[x]]++; 39 for(i = 1; i <= m; i++) 40 { 41 while(x < q[i].l) 42 { 43 ton[a[x]]--; 44 if(!ton[a[x]]) ans--; 45 x++; 46 } 47 while(x > q[i].l) 48 { 49 x--; 50 if(!ton[a[x]]) ans++; 51 ton[a[x]]++; 52 } 53 while(y > q[i].r) 54 { 55 ton[a[y]]--; 56 if(!ton[a[y]]) ans--; 57 y--; 58 } 59 while(y < q[i].r) 60 { 61 y++; 62 if(!ton[a[y]]) ans++; 63 ton[a[y]]++; 64 } 65 anslist[q[i].num] = ans; 66 } 67 for(i = 1; i <= m; i++) printf("%d\n", anslist[i]); 68 return 0; 69 }
代码量真是友善啊
还可以用离线用树状数组来做.
以下是hzwer的解法:
这题首先在线是没法做的,所以我们可以考虑离线算法
首先记录下每种颜色的下一种颜色所在的位置
将所有询问按照左端点进行排序
将所有颜色的第一个点x a[x]++
然后从左往右扫
扫到一个点x将a[next[x]]++
碰到一个询问l,r输出sum[r]-sum[l-1]
其中sum是a数组的前缀和
求前缀和可以用树状数组
——代码
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 6 const int MAXN = 50001; 7 int n, m, max; 8 int head[MAXN], next[MAXN], a[MAXN], ans[MAXN], c[1000001]; 9 struct node 10 { 11 int l, r, id; 12 }q[MAXN]; 13 14 inline int read() 15 { 16 int x = 0, f = 1; 17 char ch = getchar(); 18 for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1; 19 for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0'; 20 return x * f; 21 } 22 23 inline int Max(int x, int y) 24 { 25 return x > y ? x : y; 26 } 27 28 inline void update(int x) 29 { 30 for(; x <= n; x += x & -x) c[x]++; 31 } 32 33 inline int query(int x) 34 { 35 int ret = 0; 36 for(; x; x -= x & -x) ret += c[x]; 37 return ret; 38 } 39 40 inline bool cmp(node x, node y) 41 { 42 return x.l < y.l; 43 } 44 45 int main() 46 { 47 int i, j; 48 n = read(); 49 memset(head, -1, sizeof(head)); 50 for(i = 1; i <= n; i++) a[i] = read(), max = Max(max, a[i]); 51 for(i = n; i; i--) next[i] = head[a[i]], head[a[i]] = i; 52 for(i = 1; i <= max; i++) 53 if(head[i] ^ -1) 54 update(head[i]); 55 m = read(); 56 for(i = 1; i <= m; i++) 57 { 58 q[i].l = read(); 59 q[i].r = read(); 60 q[i].id = i; 61 } 62 std::sort(q + 1, q + m + 1, cmp); 63 j = 1; 64 for(i = 1; i <= n; i++) 65 { 66 while(q[j].l == i) ans[q[j].id] = query(q[j].r) - query(q[j].l - 1), j++; 67 if(next[i] ^ -1) update(next[i]); 68 } 69 for(i = 1; i <= m; i++) printf("%d\n", ans[i]); 70 return 0; 71 }
主席树也是个好方法。
保存每个点的上一个和它颜色相同的点的位置(如果没有就是0)
然后以每个点的前驱为下标建主席数。
统计时只需要统计当前区间 [x,y] 中前驱小于 x 的点的个数
——代码
1 #include <cstdio> 2 #include <iostream> 3 4 const int MAXN = 50001; 5 int n, m, cnt; 6 int a[MAXN], head[1000001], pre[MAXN], root[MAXN], sum[MAXN * 20], ls[MAXN * 20], rs[MAXN * 20]; 7 8 inline int read() 9 { 10 int x = 0, f = 1; 11 char ch = getchar(); 12 for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1; 13 for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0'; 14 return x * f; 15 } 16 17 inline void update(int &now, int l, int r, int x) 18 { 19 ++cnt; 20 sum[cnt] = sum[now] + 1; 21 ls[cnt] = ls[now]; 22 rs[cnt] = rs[now]; 23 now = cnt; 24 if(l == r) return; 25 int mid = (l + r) >> 1; 26 if(x <= mid) update(ls[now], l, mid, x); 27 else update(rs[now], mid + 1, r, x); 28 } 29 30 inline int query(int x, int y, int l, int r, int k) 31 { 32 if(l == r) return sum[y] - sum[x]; 33 int mid = (l + r) >> 1; 34 if(k <= mid) return query(ls[x], ls[y], l, mid, k); 35 else return query(rs[x], rs[y], mid + 1, r, k) + sum[ls[y]] - sum[ls[x]]; 36 } 37 38 int main() 39 { 40 int i, x, y; 41 n = read(); 42 for(i = 1; i <= n; i++) 43 { 44 a[i] = read(); 45 pre[i] = head[a[i]]; 46 head[a[i]] = i; 47 } 48 for(i = 1; i <= n; i++) 49 { 50 root[i] = root[i - 1]; 51 update(root[i], 0, n, pre[i]); 52 } 53 m = read(); 54 for(i = 1; i <= m; i++) 55 { 56 x = read(); 57 y = read(); 58 printf("%d\n", query(root[x - 1], root[y], 0, n, x - 1)); 59 } 60 return 0; 61 }