hdu5338 (二进制,双指针)

这种双循环的优化问题碰到过很多了。层出不穷。 但无非就是要利用前面循环时,所产生的信息,从而减少计算。

可以注意到log其实是不超过40的, 那么以这方面入手,时间复杂度就可以降为nlogn

log=4的区间肯定是log=1的区间加元素而来的,肯定是log=2的区间加元素而来的,肯定是log=3的区间加元素而来的,肯定是log=4的区间增加元素而来的。

可以发现刚好有4个区间可以变为log=4

所以每次计算log1,log2,log3,log4的时候, 后面的区间肯定是包含它们的

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <algorithm>
 5 #include <iostream>
 6 #include <queue>
 7 #include <stack>
 8 #include <vector>
 9 #include <map>
10 #include <set>
11 #include <string>
12 #include <math.h>
13 using namespace std;
14 #pragma warning(disable:4996)
15 #pragma comment(linker, "/STACK:1024000000,1024000000")
16 typedef long long LL;                   
17 const int INF = 1<<30;
18 /*
19 
20 */
21 const int N = 100000 + 10;
22 int a[N];
23 LL sum[N];
24 
25 int main()
26 {
27     
28     int t, n;
29     scanf("%d", &t);
30     LL ans;
31     while (t--)
32     {
33         ans = 0;
34         scanf("%d", &n);
35         sum[n + 1] = 0;
36         for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
37         for (int i = n; i >= 1; --i)
38         {
39             sum[i] = sum[i + 1] + i;
40             //计算所有的1*(i+j), 因为log取整之后有+1
41             ans += (LL)(n - i + 1)*i + sum[i];
42         }
43         
44         for (int k = 1; k < 40; ++k)
45         {
46             LL lim = 1LL << k;
47             LL s = 0;
48             for (int i = 1, j = 1; i <= n; ++i)
49             {
50                 while (j <= n &&s < lim)
51                     s += a[j++];
52                 if (s >= lim)//[i,j-1->n]的区间肯定是大于等于lim的
53                     ans += (LL)(n- j + 2) * i + sum[j - 1];
54                 else
55                     break;
56                 s -= a[i];
57             }
58         }
59         printf("%lld
", ans);
60     }
61     return 0;
62 }
View Code
原文地址:https://www.cnblogs.com/justPassBy/p/4710003.html