当前位置:   article > 正文

二、二分和前缀和_二分 前缀和

二分 前缀和

目录

目录

二分:

一、二分步骤

二、二分类型

1、第一类:ans是红色区间的右端点

2、第二类:ans是绿色区间的左端点

3、遇到问题思考顺序

三、二分例题

1、789. 数的范围

2、790.数的三次方根

3、730.机器人跳跃问题

4、1221. 四平方和

5、1227.分巧克力 

6、P1873 [COCI 2011/2012 #5] EKO / 砍树 

7、P1102 A-B 数对 

8、P2440 木材加工

10、P2678 [NOIP2015 提高组] 跳石头 

前缀和:

一、例题

1、795.前缀和

2、796.子矩阵的和

3、730.机器人跳跃问题

4、1230.K倍区间

5、99.激光炸弹


二分:

一、二分步骤

  1. 确定一个区间,使得目标值一定在区间中
  2. 找一个性质,满足性质:一定具有二段性,答案是二段性的分界点。

二、二分类型

1、第一类:ans是红色区间的右端点

  1. int m = l + r / 2;
  2. while(l < r)
  3. 将[l , r]分成[l, m - 1] 和 [m, r]

2、第二类:ans是绿色区间的左端点

  1. int m = l + r / 2;
  2. while(l < r)
  3. 将[l , r]分成[l, m] 和 [m + 1, r]

3、遇到问题思考顺序

二分,dfs,dp,贪心

三、二分例题

1、789. 数的范围

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<vector>
  6. using namespace std;
  7. int main() {
  8. double l = - 10000;
  9. double r = 10000;
  10. double n;
  11. cin >> n;
  12. while (r - l > 1e-8) {
  13. double mid = (l + r) / 2;
  14. if ( mid * mid * mid >= n) { // 比如说 mid = 0,而 0 < 1000,
  15. //所以左边界 = mid,再比如说:mid = 500,而500 * 500 * 500 >= 1000,所以右边界 = mid;
  16. r = mid;
  17. }
  18. else
  19. l = x;
  20. }
  21. printf("%.6lf",l);
  22. return 0;
  23. }

2、790.数的三次方根

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<algorithm>
  4. #include<cstring>
  5. #include<vector>
  6. using namespace std;
  7. int main() {
  8. vector<int> a;
  9. int n;
  10. cin >> n;
  11. int q;
  12. cin >> q;
  13. a.resize(n);
  14. for (int i = 0; i < n; i ++){
  15. cin >> a[i];
  16. }
  17. int x;
  18. int l = 0;
  19. int r = n - 1;
  20. while (q--) {
  21. cin >> x;
  22. while (l < r) {
  23. int mid = l + r >> 1;
  24. if (a[mid] >= x) r = mid;
  25. else
  26. l = mid + 1;
  27. }
  28. if (a[l] == x) {
  29. cout << l << " ";
  30. r = n - 1;
  31. while (l < r) {
  32. int mid = l + r + 1>> 1;
  33. if (a[mid] <= x) l = mid;
  34. else
  35. r = mid - 1;
  36. }
  37. cout << r << endl;
  38. }
  39. else
  40. cout << "-1 -1" << endl;
  41. }
  42. return 0;
  43. }

3、730.机器人跳跃问题

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<cmath>
  5. #include<vector>
  6. using namespace std;
  7. const int N=1e5+10;
  8. int h[N],n;
  9. bool check(int mid)//判定能量是否够用
  10. {
  11. for(int i = 1; i <= n; i++)
  12. {
  13. mid = 2 * mid - h[i];
  14. if(mid < 0) return false; //如果小于,就说明不够用
  15. else if(mid >= 1e5) return true; // 够用
  16. }
  17. return true;
  18. }
  19. int main()
  20. {
  21. cin >> n;
  22. for(int i = 1; i <= n; i++)
  23. cin >> h[i];
  24. int l = 0,r = 1e5;//二分边界
  25. while(i < j)
  26. {
  27. int mid = l + r >> 1;
  28. if(check(mid)) l = mid;
  29. else r = mid + 1;
  30. }
  31. cout << l;
  32. return 0;
  33. }

4、1221. 四平方和

  1. #include<iostream>
  2. using namespace std;
  3. #include<unordered_map>
  4. typedef pair <int ,int > PII;
  5. unordered_map <int, PII> s;
  6. int main()
  7. {
  8. int n;
  9. cin >> n;
  10. int a, b, c, d;
  11. for (int c = 0 ; c <= n; c ++)
  12. for(int d = c; d * d + c * c <= n; d++) {
  13. int t = c * c + d * d;
  14. if(s.count(t) == 0) // 记录第一次出现的t和数字,因为是从小到大,所以说一定符合题意
  15. s[t] ={c, d};
  16. }
  17. for (int a = 0 ; a <= n; a ++)
  18. for(int b = a; a * a + b * b <= n; b ++) {
  19. int t = n - a * a + b * b;
  20. if(s.count(t)) { // 如果这个数不等于0,说明找到了
  21. cout << a << " "<< b <<" "<< s[t].first<<" "<< s[t].second;
  22. return 0;
  23. }
  24. }
  25. return 0;
  26. }

5、1227.分巧克力 

  1. #include <iostream>
  2. #include <algorithm>
  3. using namespace std;
  4. const int N = 100010;
  5. int n, k; // 有n块巧克力,k个小朋友
  6. int wid[N], len[N]; // 分别保存巧克力的长和宽
  7. // 计算当正方形的边长为u时,总共能分得多少块巧克力
  8. int get_sum(int u) {
  9. int sum = 0;
  10. for (int i = 0; i < n; i++) {
  11. // 巧克力块数
  12. sum += ((wid[i] / u) * (len[i] / u));
  13. }
  14. return sum;
  15. }
  16. int main() {
  17. cin >> n;
  18. cin >> k;
  19. for (int i = 0; i < n; i++) {
  20. cin >> wid[i];
  21. cin >> len[i];
  22. }
  23. // 二分模板
  24. // 性质是“当边长取mid时,能够分得k块巧克力”
  25. // 显然在一个数轴上,目标值的左侧(比目标值小的数)都是满足这个性质的,右边则不满足
  26. // 所以我们要找的值是满足这个性质的最右端
  27. // 如果当前取的mid满足这个性质,那么l有可能就是我们要找的目标值,所以用l = mid来更新
  28. // 综上所述,用以下模板
  29. int l = 0, r = N;
  30. while (l < r) {
  31. int mid = (l + r + 1) >> 1;
  32. if (get_sum(mid) >= k) {
  33. l = mid;
  34. } else {
  35. r = mid - 1;
  36. }
  37. }
  38. printf("%d\n", l);
  39. return 0;
  40. }

6、P1873 [COCI 2011/2012 #5] EKO / 砍树 

  1. #include<iostream>
  2. #include<bits/stdc++.h>
  3. using namespace std;
  4. int n,m;
  5. const int N = 10000100;
  6. int a[N];
  7. bool check(int x) {
  8. int sum = 0;
  9. for (int i = 1; i <= n; i++) {
  10. sum += max(0,a[i] - x);
  11. if (sum >= m)
  12. return true;
  13. }
  14. return false;
  15. }
  16. long long sum = 0;
  17. int main() {
  18. cin >> n >> m;
  19. int maxt = 0;
  20. for (int i = 1; i <= n; i++) {
  21. cin >> a[i];
  22. maxt = max(a[i],maxt);
  23. }
  24. sort(a + 1, a + 1 + n);
  25. int i;
  26. int res = 0;
  27. int l = 0,r = maxt + 1;
  28. while (l + 1 < r ){
  29. int mid = l + r >> 1;
  30. if (check(mid))
  31. l = mid;
  32. else
  33. r = mid;
  34. }
  35. if (check(r)) cout << r << endl;
  36. else cout << l << endl;
  37. return 0;
  38. }

7、P1102 A-B 数对 

  1. #include<iostream>
  2. #include<bits/stdc++.h>
  3. using namespace std;
  4. int n,c;
  5. const int N = 1000010;
  6. int a[N];
  7. int search1(int x) {
  8. int l = 0,r = n + 1;
  9. while (l + 1 < r) {
  10. int mid = (l + r) >> 1;
  11. if (a[mid] < x)
  12. l = mid;
  13. else
  14. r = mid;
  15. }
  16. if (a[r] == x)
  17. return r;
  18. else
  19. return -1;
  20. }
  21. int search2(int x) {
  22. int l = 0,r = n + 1;
  23. while (l + 1 < r) {
  24. int mid = (l + r) >> 1;
  25. if (a[mid] <= x)
  26. l = mid;
  27. else
  28. r = mid;
  29. }
  30. if (a[l] == x)
  31. return l;
  32. else
  33. return -1;
  34. }
  35. int main() {
  36. cin >> n >> c;
  37. for (int i = 1; i <= n; i++) {
  38. cin >> a[i];
  39. }
  40. sort(a + 1,a + 1 + n);
  41. long long cnt = 0;
  42. for (int b = 1; b <= n; b++) {
  43. int A = a[b]+ c;
  44. int res1 = search1(A);
  45. if (res1 == -1)
  46. continue;
  47. else {
  48. int res2 = search2(A);
  49. cnt += res2 - res1 + 1;
  50. }
  51. }
  52. cout << cnt << endl;
  53. return 0;
  54. }

8、P2440 木材加工

  1. #include<iostream>
  2. using namespace std;
  3. const int N = 100010;
  4. int a[N];
  5. int n,k;
  6. long long sum = 0;
  7. bool check(int x) {
  8. int sum = 0;
  9. for (int i = 1; i <= n; i ++) {
  10. sum += a[i] / x;
  11. if (sum >= k) return true;
  12. }
  13. return false;
  14. }
  15. int main() {
  16. cin >> n >> k;
  17. int longest = 0;
  18. for (int i = 1; i <= n; i++) {
  19. cin >> a[i];
  20. longest = max(a[i],longest);
  21. }
  22. int sum = 0;
  23. int res = 0;
  24. int l = 0,r = longest;
  25. while (l + 1 != r) {
  26. int mid = l + r >> 1;
  27. if (check(mid)) {
  28. l = mid;
  29. }
  30. else
  31. r = mid;
  32. }
  33. if (check(r)) cout << r << endl;
  34. else cout << l << endl;
  35. return 0;
  36. }

10、P2678 [NOIP2015 提高组] 跳石头 

前缀和:

一、例题

1、795.前缀和

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<cstring>
  4. #include<algorithm>
  5. #include<vector>
  6. using namespace std;
  7. int main() {
  8. int n, m;
  9. cin >> n;
  10. int x;
  11. cin >> m;
  12. vector<int> s(n + 10);
  13. for (int i = 1; i <= n; i++) {
  14. cin >> x;
  15. s[i] = s[i - 1] + x;
  16. }
  17. int l,r;
  18. while (m--) {
  19. cin >> l;
  20. cin >> r;
  21. cout << s[r] - s[l - 1] << endl;
  22. }
  23. return 0;
  24. }

2、796.子矩阵的和

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>
  4. #include<vector>
  5. #include<iostream>
  6. using namespace std;
  7. #define N 1010
  8. int main() {
  9. int n, m, q;
  10. cin >> n;
  11. cin >> m;
  12. cin >> q;
  13. int a[N][N] = {0};
  14. int s[N][N] = {0};
  15. for (int i = 1; i <= n; i++) {
  16. for (int j = 1; j <= m; j++) {
  17. cin >> a[i][j];
  18. s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
  19. }
  20. }
  21. int x1, x2, y1,y2;
  22. while (q--) {
  23. cin >> x1 ;
  24. cin >> y1;
  25. cin >> x2;
  26. cin >> y2;
  27. cout << s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]<< endl;
  28. }
  29. return 0;
  30. }

3、730.机器人跳跃问题

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<cmath>
  5. #include<vector>
  6. using namespace std;
  7. const int N=1e5+10;
  8. int h[N],n;
  9. bool check(int mid)//判定能量是否够用
  10. {
  11. for(int i = 1; i <= n; i++)
  12. {
  13. mid = 2 * mid - h[i];
  14. if(mid < 0) return false;
  15. else if(mid >= 1e5) return true;
  16. }
  17. return true;
  18. }
  19. int main()
  20. {
  21. cin >> n;
  22. for(int i = 1; i <= n; i++)
  23. cin >> h[i];
  24. int i = 0,j = 1e5;//二分边界
  25. while(i < j)
  26. {
  27. int mid = i + j >> 1;
  28. if(check(mid)) j = mid;
  29. else i = mid + 1;
  30. }
  31. cout << j;
  32. return 0;
  33. }

4、1230.K倍区间

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <iostream>
  4. #include <algorithm>
  5. using namespace std;
  6. typedef long long LL;
  7. const int N = 100010;
  8. int n, k;
  9. LL s[N], cnt[N];
  10. // cnt的意义:存储模k的值,将其作为左端点(模k左端点)的数量
  11. // for的意义:遍历每个端点,先将其作为模k右端点,根据其模k的值查看它有
  12. // 多少个模k左端点,即能形成多少个模k区间,然后将其作为模k左端点,数量加1
  13. // for前cnt[0] = 1的意义:当遍历出首个为k倍的前缀和时,它不需要模k左端点即可形成模k区间,为满足//通解将0作为其模k左端点,故cnt[0] ++
  14. int main()
  15. {
  16. scanf("%d%d", &n, &k);
  17. for (int i = 1; i <= n; i ++ )
  18. {
  19. scanf("%lld", &s[i]);
  20. s[i] += s[i - 1];
  21. }
  22. LL res = 0;
  23. cnt[0] = 1;
  24. for (int i = 1; i <= n; i ++ )
  25. {
  26. res += cnt[s[i] % k];
  27. cnt[s[i] % k] ++ ;
  28. }
  29. cout << res;
  30. return 0;
  31. }

5、99.激光炸弹

  1. #include <cstring>
  2. #include <iostream>
  3. #include <algorithm>
  4. using namespace std;
  5. const int N = 5010;
  6. int n, m;
  7. int s[N][N];
  8. int main()
  9. {
  10. int cnt, R;
  11. cin >> cnt >> R;
  12. R = min(5001, R);
  13. n = m = R;
  14. while (cnt -- )
  15. {
  16. int x, y, w;
  17. cin >> x >> y >> w;
  18. x ++, y ++ ;
  19. n = max(n, x), m = max(m, y);
  20. s[x][y] += w;
  21. }
  22. // 预处理前缀和
  23. for (int i = 1; i <= n; i ++ )
  24. for (int j = 1; j <= m; j ++ )
  25. s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
  26. int res = 0;
  27. // 枚举所有边长是R的矩形,枚举(i, j)为右下角
  28. for (int i = R; i <= n; i ++ )
  29. for (int j = R; j <= m; j ++ )
  30. res = max(res, s[i][j] - s[i - R][j] - s[i][j - R] + s[i - R][j - R]);
  31. cout << res << endl;
  32. return 0;
  33. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/620892
推荐阅读
相关标签
  

闽ICP备14008679号