这种双循环的优化问题碰到过很多了。层出不穷。 但无非就是要利用前面循环时,所产生的信息,从而减少计算。
可以注意到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 }