square root algorithm根号n算法

image.png

image.png
image.png
image.png

mo`s algorithm莫队

https://zhuanlan.zhihu.com/p/115243708
https://www.cnblogs.com/WAMonster/p/10118934.html
https://www.luogu.com.cn/blog/DPair2005/talk-talk-bruh-forces-in-oi

例题:奇数

image.png
image.png
image.png

询问离线后,排序的不同优化

  1. // 询问离线后,排序的不同优化
  2. // 用时5038ms
  3. // 左端点在同一个块里的询问 ,我们按它们的 右端点升序排序
  4. // 否则我们按它们的 左端点升序排序
  5. struct node{
  6. int l, r, idx;
  7. bool operator< (const node &W) const{
  8. if (l / B == W.l / B) return r < W.r;
  9. return l < W.l;
  10. }
  11. }q[N];
  12. // 用时4879ms
  13. // 不难发现,排序的时候,我们每一个块都是按右端点升序排序的,
  14. // 这里显然只需要保证右端点单调就行了,所以降序排序也是对的。
  15. struct node{
  16. int l, r, idx;
  17. bool operator< (const node &W) const{
  18. if (l / B == W.l / B) return r > W.r;
  19. return l < W.l;
  20. }
  21. }q[N];
  22. // 用时3322ms
  23. // 然后我们又不难发现,每一次我们处理完左端点在某一个块中的所有询问后,
  24. // 右端点此时应该在序列的靠右端,处理下一个块时又回到最左端了,然后又不断往右,有一些浪费。
  25. // 所以我们考虑引入“奇偶块排序”,即对于左端点编号为奇数的块升序,左端点编号为偶数的块降序排序。
  26. // 关于小技巧的说明:对于两个区间交替的过程中,
  27. // r如果先回来,那么部分点就会被扫两次,
  28. // 那么对于r一正一逆的排序,就可以避免重复的情况
  29. struct node{
  30. int l, r, idx;
  31. bool operator< (const node &W) const{
  32. if (l / B != W.l / B) return l < W.l;
  33. if (l / B & 1) return r < W.r;
  34. return r > W.r;
  35. }
  36. }q[N];

没有离散化的AC代码,数据有点锅

  1. // 没有离散化的
  2. #include <bits/stdc++.h>
  3. using namespace std;
  4. const int N = 1e5 + 10;
  5. int a[N], cnt[N], n, m;
  6. int B;
  7. int l = 1, r = 0;
  8. int ans[N], cur;
  9. struct node{
  10. int l, r, idx;
  11. bool operator< (const node &W) const{
  12. //if (l / B == W.l / B) return r > W.r;
  13. //return l < W.l;
  14. if (l / B != W.l / B) return l < W.l;
  15. if (l / B & 1) return r < W.r;
  16. return r > W.r;
  17. }
  18. }q[N];
  19. void add(int p){
  20. cnt[a[p]]++;
  21. if (cnt[a[p]] % 2 == 0) cur--;
  22. else cur++;
  23. }
  24. void del(int p){
  25. cnt[a[p]]--;
  26. if (cnt[a[p]] % 2 == 0) cur--;
  27. else cur++;
  28. }
  29. int main(){
  30. cin >> n;
  31. B = sqrt(n);
  32. for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
  33. cin >> m;
  34. for (int i = 0; i < m; i++){
  35. scanf("%d%d", &q[i].l, &q[i].r);
  36. q[i].idx = i;
  37. }
  38. sort(q, q + m);
  39. for (int i = 0; i < m; i++){
  40. while (l > q[i].l) add(--l);
  41. while (r < q[i].r) add(++r);
  42. while (l < q[i].l) del(l++);
  43. while (r > q[i].r) del(r--);
  44. ans[q[i].idx] = cur;
  45. }
  46. for (int i = 0; i < m; i++) printf("%d\n", ans[i]);
  47. return 0;
  48. }

加入离散化后的AC代码

  1. // 加入离散化
  2. #include <bits/stdc++.h>
  3. using namespace std;
  4. const int N = 1e5 + 10;
  5. int a[N], cnt[N], n, m;
  6. int B;
  7. int l = 1, r = 0;
  8. int ans[N], cur;
  9. int lsh[N];
  10. struct node{
  11. int l, r, idx;
  12. bool operator< (const node &W) const{
  13. if (l / B != W.l / B) return l < W.l;
  14. if (l / B & 1) return r < W.r;
  15. return r > W.r;
  16. }
  17. }q[N];
  18. void add(int p){
  19. cnt[a[p]]++;
  20. if (cnt[a[p]] % 2 == 0) cur--;
  21. else cur++;
  22. }
  23. void del(int p){
  24. cnt[a[p]]--;
  25. if (cnt[a[p]] % 2 == 0) cur--;
  26. else cur++;
  27. }
  28. int main(){
  29. cin >> n;
  30. B = sqrt(n);
  31. for (int i = 1; i <= n; i++){
  32. scanf("%d", &a[i]);
  33. lsh[i] = a[i];
  34. }
  35. sort(lsh + 1, lsh + 1 + n);
  36. int num = unique(lsh + 1, lsh + 1 + n) - lsh - 1;
  37. for (int i = 1; i <= n; i++)
  38. a[i] = lower_bound(lsh+1, lsh+num+1, a[i]) - lsh; //解决掉a[i]<0的问题
  39. cin >> m;
  40. for (int i = 0; i < m; i++){
  41. scanf("%d%d", &q[i].l, &q[i].r);
  42. q[i].idx = i;
  43. }
  44. sort(q, q + m);
  45. for (int i = 0; i < m; i++){
  46. while (l > q[i].l) add(--l);
  47. while (r < q[i].r) add(++r);
  48. while (l < q[i].l) del(l++);
  49. while (r > q[i].r) del(r--);
  50. ans[q[i].idx] = cur;
  51. }
  52. for (int i = 0; i < m; i++) printf("%d\n", ans[i]);
  53. return 0;
  54. }

加入快读,用时3306ms

  1. // 使用快读
  2. // 用时3306ms
  3. #include <bits/stdc++.h>
  4. using namespace std;
  5. const int N = 1e5 + 10;
  6. int a[N], cnt[N], n, m;
  7. int B;
  8. int l = 1, r = 0;
  9. int ans[N], cur;
  10. int lsh[N];
  11. inline int read(){
  12. int f = 1, x = 0;
  13. char c = getchar();
  14. while (c < '0' || c > '9'){
  15. if (c == '-') f = -1;
  16. c = getchar();
  17. }
  18. while (c >= '0' && c <= '9'){
  19. x = x * 10 + c - '0';
  20. c = getchar();
  21. }
  22. return f * x;
  23. }
  24. struct node{
  25. int l, r, idx;
  26. bool operator< (const node &W) const{
  27. //if (l / B == W.l / B) return r < W.r;
  28. //return l < W.l;
  29. if (l / B != W.l / B) return l < W.l;
  30. if (l / B & 1) return r < W.r;
  31. return r > W.r;
  32. }
  33. }q[N];
  34. void add(int p){
  35. cnt[a[p]]++;
  36. if (cnt[a[p]] % 2 == 0) cur--;
  37. else cur++;
  38. }
  39. void del(int p){
  40. cnt[a[p]]--;
  41. if (cnt[a[p]] % 2 == 0) cur--;
  42. else cur++;
  43. }
  44. int main(){
  45. n = read();
  46. B = sqrt(n);
  47. for (int i = 1; i <= n; i++){
  48. a[i] = read();
  49. lsh[i] = a[i];
  50. }
  51. sort(lsh + 1, lsh + 1 + n);
  52. int num = unique(lsh + 1, lsh + 1 + n) - lsh - 1;
  53. for (int i = 1; i <= n; i++)
  54. a[i] = lower_bound(lsh+1, lsh+num+1, a[i]) - lsh; //解决掉a[i]<0的问题
  55. m = read();
  56. for (int i = 0; i < m; i++){
  57. q[i].l = read(); q[i].r = read();
  58. q[i].idx = i;
  59. }
  60. sort(q, q + m);
  61. for (int i = 0; i < m; i++){
  62. while (l > q[i].l) add(--l);
  63. while (r < q[i].r) add(++r);
  64. while (l < q[i].l) del(l++);
  65. while (r > q[i].r) del(r--);
  66. ans[q[i].idx] = cur;
  67. }
  68. for (int i = 0; i < m; i++) printf("%d\n", ans[i]);
  69. return 0;
  70. }

加入inline,用时2606ms

在 c/c++ 中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,表示为内联函数。栈空间就是指放置程序的局部数据(也就是函数内数据)的内存空间。

  1. // 使用inline
  2. // 用时2606ms
  3. #include <bits/stdc++.h>
  4. using namespace std;
  5. const int N = 1e5 + 10;
  6. int a[N], cnt[N], n, m;
  7. int B;
  8. int l = 1, r = 0;
  9. int ans[N], cur;
  10. int lsh[N];
  11. inline int read(){
  12. int f = 1, x = 0;
  13. char c = getchar();
  14. while (c < '0' || c > '9'){
  15. if (c == '-') f = -1;
  16. c = getchar();
  17. }
  18. while (c >= '0' && c <= '9'){
  19. x = x * 10 + c - '0';
  20. c = getchar();
  21. }
  22. return f * x;
  23. }
  24. struct node{
  25. int l, r, idx;
  26. bool operator< (const node &W) const{
  27. //if (l / B == W.l / B) return r < W.r;
  28. //return l < W.l;
  29. if (l / B != W.l / B) return l < W.l;
  30. if (l / B & 1) return r < W.r;
  31. return r > W.r;
  32. }
  33. }q[N];
  34. inline void add(int p){
  35. cnt[a[p]]++;
  36. if (cnt[a[p]] % 2 == 0) cur--;
  37. else cur++;
  38. }
  39. inline void del(int p){
  40. cnt[a[p]]--;
  41. if (cnt[a[p]] % 2 == 0) cur--;
  42. else cur++;
  43. }
  44. int main(){
  45. n = read();
  46. B = sqrt(n);
  47. for (int i = 1; i <= n; i++){
  48. a[i] = read();
  49. lsh[i] = a[i];
  50. }
  51. sort(lsh + 1, lsh + 1 + n);
  52. int num = unique(lsh + 1, lsh + 1 + n) - lsh - 1;
  53. for (int i = 1; i <= n; i++)
  54. a[i] = lower_bound(lsh+1, lsh+num+1, a[i]) - lsh; //解决掉a[i]<0的问题
  55. m = read();
  56. for (int i = 0; i < m; i++){
  57. q[i].l = read(); q[i].r = read();
  58. q[i].idx = i;
  59. }
  60. sort(q, q + m);
  61. for (int i = 0; i < m; i++){
  62. while (l > q[i].l) add(--l);
  63. while (r < q[i].r) add(++r);
  64. while (l < q[i].l) del(l++);
  65. while (r > q[i].r) del(r--);
  66. ans[q[i].idx] = cur;
  67. }
  68. for (int i = 0; i < m; i++) printf("%d\n", ans[i]);
  69. return 0;
  70. }

加入分块,用时2529ms

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1e5 + 10;
  4. int a[N], cnt[N], n, m;
  5. int B;
  6. int l = 1, r = 0;
  7. int ans[N], cur;
  8. int lsh[N];
  9. int bel[N];
  10. inline int read(){
  11. int f = 1, x = 0;
  12. char c = getchar();
  13. while (c < '0' || c > '9'){
  14. if (c == '-') f = -1;
  15. c = getchar();
  16. }
  17. while (c >= '0' && c <= '9'){
  18. x = x * 10 + c - '0';
  19. c = getchar();
  20. }
  21. return f * x;
  22. }
  23. // [l, r]这个区间,进行查找
  24. // bel[l] bel[W.l]根据所在的块进行区分
  25. struct node{
  26. int l, r, idx;
  27. bool operator< (const node &W) const{
  28. if (bel[l] != bel[W.l]) return bel[l] < bel[W.l];
  29. if (bel[l] & 1) return r < W.r;
  30. return r > W.r;
  31. }
  32. }q[N];
  33. inline void add(int p){
  34. cnt[a[p]]++;
  35. if (cnt[a[p]] % 2 == 0) cur--;
  36. else cur++;
  37. }
  38. inline void del(int p){
  39. cnt[a[p]]--;
  40. if (cnt[a[p]] % 2 == 0) cur--;
  41. else cur++;
  42. }
  43. int main(){
  44. n = read();
  45. B = sqrt(n);
  46. // 分块
  47. for (int i = 1; i <= n; i++) bel[i] = (i - 1) / B + 1;
  48. for (int i = 1; i <= n; i++){
  49. a[i] = read();
  50. lsh[i] = a[i];
  51. }
  52. sort(lsh + 1, lsh + 1 + n);
  53. int num = unique(lsh + 1, lsh + 1 + n) - lsh - 1;
  54. for (int i = 1; i <= n; i++)
  55. a[i] = lower_bound(lsh+1, lsh+num+1, a[i]) - lsh; //解决掉a[i]<0的问题
  56. m = read();
  57. for (int i = 0; i < m; i++){
  58. q[i].l = read(); q[i].r = read(); q[i].idx = i;
  59. }
  60. sort(q, q + m);
  61. for (int i = 0; i < m; i++){
  62. while (l > q[i].l) add(--l);
  63. while (r < q[i].r) add(++r);
  64. while (l < q[i].l) del(l++);
  65. while (r > q[i].r) del(r--);
  66. ans[q[i].idx] = cur;
  67. }
  68. for (int i = 0; i < m; i++) printf("%d\n", ans[i]);
  69. return 0;
  70. }


例题:索引

image.png
image.png
image.png

这道题目,本意是二分,我们在处理多次区间修改的过程中,可以用分块处理

  1. // 分块
  2. #include <bits/stdc++.h>
  3. using namespace std;
  4. const int N = 1e7 + 10;
  5. int k, n, a[N];
  6. int bel[N], tag[N];
  7. int B;
  8. inline int read(){
  9. int f = 1, x = 0;
  10. char c = getchar();
  11. while (c < '0' || c > '9'){
  12. if (c == '-') f = -1;
  13. c = getchar();
  14. }
  15. while (c >= '0' && c <= '9'){
  16. x = x * 10 + c - '0';
  17. c = getchar();
  18. }
  19. return f * x;
  20. }
  21. // 核心是这个环节,特别有趣
  22. // 体现了O(n * sqrt(n))的思想
  23. void update(int l, int r, int c){
  24. for (int i = l; i <= min(bel[l] * B, r); i++) a[i] += c;
  25. if (bel[l] != bel[r]) for (int i = (bel[r]-1) * B + 1; i <= r; i++) a[i] += c;
  26. for (int i = bel[l] + 1; i <= bel[r] - 1; i++) tag[i] += c;
  27. }
  28. void ask(){
  29. int l = 1, r = n, best = 1;
  30. while (l < r){
  31. int mid = (l + r + 1) >> 1;
  32. if (a[mid] + tag[bel[mid]] <= 0) l = mid, best = mid;
  33. else r = mid - 1;
  34. }
  35. if (a[best] + tag[bel[best]] == 0) puts("YES");
  36. else puts("NO");
  37. //for (int i = 1; i <= n; i++) printf("%d ", a[i]);
  38. //puts("");
  39. }
  40. int main(){
  41. k = read(), n = read();
  42. B = sqrt(n);
  43. for (int i = 1; i <= n; i++){
  44. a[i] = read() - i;
  45. bel[i] = (i - 1) / B + 1;
  46. }
  47. ask();
  48. for (int i = 2; i <= k; i++){
  49. int l = read(), r = read(), c = read();
  50. update(l, r, c);
  51. ask();
  52. }
  53. return 0;
  54. }