题目链接:点击打开链接
题目大意:给出一个序列,其中S(i,j)代表a[i]到a[j]的和,然后计算的和。
其中包含了向下取整,所以我们可以想到,log(2,S(i,j))向下取整后最大也就是33,所以我们可以寻找log值相同的段,
对于以每一个数为起点,都有可能存在一段连续的序列,他们的log值是相同的,,,一开始的做法是枚举起点i,然后对每一段log值计算一个区间(r1,r2),通过二分找到这个区间,计算出和,那么时间复杂度是n*log(n)*log(n),妥妥超时了,,,,
时间一定要是严格的n*log(n)
所以可以枚举log的值0~33,对于每一个log的值,枚举开始的起点,用双指针(尺取法)找到那一段连续的序列,以j为枚举的累加位置,r1记录序列的开始的位置,r2记录刚超出序列的位置,那么从j累加到[r1,r2-1]这一段的log值就是相同的。
注意:r1要>=j,r2可以为n+1。保证[r1,r2-1]区间的r2-1可以到n
#include <cstdio> #include <cstring> #include <algorithm> using namespace std ; #define LL __int64 LL a[100010] , sum[100010] ; LL s[40] ; int main() { int t , n , i , j , r1 , r2 ; LL ans , cnt1 , cnt2 , k ; s[0] = 0 ; s[1] = 2 ; for(i = 2 ; i <= 35 ; i++) s[i] = s[i-1]*2 ; scanf("%d", &t) ; while( t-- ) { scanf("%d", &n) ; a[0] = sum[0] = 0 ; for(i = 1 ; i <= n ; i++) { scanf("%I64d", &a[i]) ; sum[i] = sum[i-1] + a[i] ; } a[n+1] = 0 ; ans = 0 ; for(i = 0 ; i < 35 ; i++) { if( s[i] > sum[n] ) break ; r1 = r2 = 1 ; cnt1 = cnt2 = a[1] ; for(j = 1 ; j <= n ; j++) { while( (r1 < j )|| r1 <= n && cnt1 < s[i] ) { r1++ ; cnt1 += a[r1] ; } if( r1 > n ) break ; while( r2 <= n && cnt2 < s[i+1] ) { r2++ ; cnt2 += a[r2] ; } if( r1 <= r2-1 ) { k = r2-r1 ; ans += k*(i+1)*j + ( k*r1+k*(k-1)/2 )*(i+1) ; } cnt1 -= a[j] ; cnt2 -= a[j] ; } } printf("%I64d\n", ans) ; } return 0 ; }