【尺取法/二分+优化】Audition SPOJ - CRAN04

冷不防 2022-06-04 04:36 291阅读 0赞

Think:
1知识点:尺取法/二分+优化(k == 0时判断小区间长度进而通过公式计算)

2题意:
(1):判断在一个长度为n(n <= 1e6)的01序列中判断有多少个区间内的1的数量为k
3思路:
(1):尺取法+(k == 0时特殊判定)
(2):二分+(k == 0时特殊判定)
4反思:
(1):尺取法需要加强理解
(2):未考虑到临界数据(eg:k == 0)时的时间复杂度
(3):未考虑到临界数据(eg:k == 0)时需要通过long long 存储满足条件的区间的数量

vjudge题目链接

以下为Accepted代码——二分+优化

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. typedef long long LL;
  6. int rec[1004014], sum[1004014];
  7. char str[1004014];
  8. int main(){
  9. LL T, n, k, ans;
  10. scanf("%lld", &T);
  11. while(T--){
  12. scanf("%lld %lld", &n, &k);
  13. scanf("%s", str);
  14. for(int i = 0; i < n; i++){
  15. rec[i+1] = str[i] - '0';
  16. }
  17. if(!k) {
  18. ans = 0;
  19. LL t, j;
  20. for(LL i = 1; i <= n; i++){
  21. if(rec[i]) continue;
  22. for(j = i+1; j <= n; j++){
  23. if(rec[j]) break;
  24. }
  25. if(j <= n) {
  26. t = (j-1)-i+1;
  27. ans += t*(t+1)/(LL)2;
  28. i = j;
  29. }
  30. else {
  31. t = n-i+1;
  32. ans += t*(t+1)/(LL)2;
  33. break;
  34. }
  35. }
  36. printf("%lld\n", ans);
  37. continue;
  38. }
  39. sum[0] = 0;
  40. for(int i = 1; i <= n; i++){
  41. sum[i] = sum[i-1] + rec[i];
  42. }
  43. ans = 0;
  44. LL t, l, r, mid, mt;
  45. for(int i = 1; i <= n; i++){
  46. t = sum[i] + k - rec[i];
  47. l = i, r = n;
  48. while(l <= r){
  49. mid = (l+r)/2;
  50. if(sum[mid] == t) break;
  51. if(sum[mid] < t) {
  52. l = mid+1;
  53. }
  54. else if(sum[mid] > t){
  55. r = mid-1;
  56. }
  57. }
  58. if(l > r) continue;
  59. else {
  60. ans++;
  61. mt = mid-1;
  62. while(mt >= i){
  63. if(sum[mt] == t) ans++;
  64. else break;
  65. mt--;
  66. }
  67. mt = mid+1;
  68. while(mt <= n){
  69. if(sum[mt] == t) ans++;
  70. else break;
  71. mt++;
  72. }
  73. }
  74. }
  75. printf("%lld\n", ans);
  76. }
  77. return 0;
  78. }

以下为Accepted代码——尺取法+优化

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. typedef long long LL;
  6. int rec[1004014];
  7. char str[1004014];
  8. int main(){
  9. int T, n, k;
  10. LL ans;
  11. scanf("%d", &T);
  12. while(T--){
  13. scanf("%d %d", &n, &k);
  14. scanf("%s", str);
  15. for(int i = 1; i <= n; i++){
  16. rec[i] = str[i-1] - '0';
  17. }
  18. ans = 0;
  19. if(!k) {
  20. LL t, j;
  21. for(LL i = 1; i <= n; i++){
  22. if(rec[i]) continue;
  23. for(j = i+1; j <= n; j++){
  24. if(rec[j]) break;
  25. }
  26. if(j <= n) {
  27. t = (j-1)-i+1;
  28. ans += t*(t+1)/(LL)2;
  29. i = j;
  30. }
  31. else {
  32. t = n-i+1;
  33. ans += t*(t+1)/(LL)2;
  34. break;
  35. }
  36. }
  37. }
  38. else {
  39. int id, cnt = 0;
  40. LL a = 0, b = 0;
  41. for(int i = 1; i <= n; i++){
  42. if(rec[i] == 1){
  43. cnt++;
  44. if(cnt == 1) id = i;
  45. else if(cnt == k+1){
  46. ans += (a+1)*(b+1);
  47. a = 0, b = 0, cnt -= 1;
  48. id++;
  49. while(id <= n && rec[id] == 0){
  50. a++;
  51. id++;
  52. }
  53. }
  54. }
  55. else {
  56. if(cnt == 0) a++;
  57. else if(cnt == k) b++;
  58. }
  59. }
  60. if(cnt == k) ans += (a+1)*(b+1);/*尺取遍历结束之后正好cnt累计到达k的情况需要考虑*/
  61. }
  62. printf("%lld\n", ans);
  63. }
  64. return 0;
  65. }

发表评论

表情:
评论列表 (有 0 条评论,291人围观)

还没有评论,来说两句吧...

相关阅读

    相关 总结

    一般地,当我们要去枚举满足条件的区间权值的区间,可以用遍历双指针 l 和 r 的做法去枚举第一个满足条件的区间,然后去维护其他数据,这样的做法就是尺取法,它的复杂度为O(n)

    相关 模板

      题目描述 博览馆正在展出由世上最佳的 M 位画家所画的图画。 wangjy想到博览馆去看这几位大师的作品。 可是,那里的博览馆有一个很奇怪的规定,就是在购买门票

    相关 算法--

    尺取法 尺取法通常是指对数组保存一对下标(起点,终点),然后根据实际情况交替推进两个端点直到得出答案的方法,这种操作很像是尺取虫爬行的方式故得名。 我们先来看看POJ的

    相关 Pie————二分+练习

    给你n个蛋糕的半径,你有m个朋友,所以总共有m+1个人,现在要分蛋糕,要求每个人分到的大小都是一样的,且每个人的蛋糕都是从一块上切割下来的(不能是2个不同的蛋糕拼起来的),现在