
这种双循环的优化问题碰到过很多了。层出不穷。 但无非就是要利用前面循环时,所产生的信息,从而减少计算。
可以注意到log其实是不超过40的, 那么以这方面入手,时间复杂度就可以降为nlogn
log=4的区间肯定是log=1的区间加元素而来的,肯定是log=2的区间加元素而来的,肯定是log=3的区间加元素而来的,肯定是log=4的区间增加元素而来的。
可以发现刚好有4个区间可以变为log=4
所以每次计算log1,log2,log3,log4的时候, 后面的区间肯定是包含它们的
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <math.h>
using namespace std;
#pragma warning(disable:4996)
#pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
const int INF = <<;
/* */
const int N = + ;
int a[N];
LL sum[N]; int main()
{ int t, n;
scanf("%d", &t);
LL ans;
while (t--)
{
ans = ;
scanf("%d", &n);
sum[n + ] = ;
for (int i = ; i <= n; ++i)scanf("%d", &a[i]);
for (int i = n; i >= ; --i)
{
sum[i] = sum[i + ] + i;
//计算所有的1*(i+j), 因为log取整之后有+1
ans += (LL)(n - i + )*i + sum[i];
} for (int k = ; k < ; ++k)
{
LL lim = 1LL << k;
LL s = ;
for (int i = , j = ; i <= n; ++i)
{
while (j <= n &&s < lim)
s += a[j++];
if (s >= lim)//[i,j-1->n]的区间肯定是大于等于lim的
ans += (LL)(n- j + ) * i + sum[j - ];
else
break;
s -= a[i];
}
}
printf("%lld\n", ans);
}
return ;
}