hdu5358--First One(双指针)

题目链接:点击打开链接

题目大意:给出一个序列,其中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 ;
}


你可能感兴趣的:(hdu5358--First One(双指针))