HDU - 5358 First One (数学+尺取)

题意:

计算    \sum_{i=1}^{n} \sum_{j=i}^{n} (\lfloor \log_2 S(i,j) \rfloor + 1) \times (i + j)   其中  \log_2 0=0  ,S(i,j) 表示a_i, a_{i+1}, \dots, a_j 的和。

分析:

因为  \lfloor \log_2 S(i,j) \rfloor + 1   表示每个数的二进制有多少位, 并且最大的数也只有34位,所以根据这个条件进行尺取,复杂度O(34*n)  尺取计算满足当前位的条件下每个(i+j) 的和,每对应一个i值,利用尺取法求出满足条件的j值的范围(a<=j<=b),则

i+j=(i+a)+(i+a+1)+(i+a+2)+ \dots+(i+b)=(b-a+1)*i+(a+b)*(b-a+1)/2

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const int maxn=1e5+10;

ll ans;
ll pw[40];
ll pre[maxn];
ll a[maxn];
int n;

ll get_sum(ll L,ll R){          //计算[L,R)中i+j的和
    int l=1,r=1;
    ll res=0;
    for(int i=1;i<=n;i++){
        l=max(l,i);r=max(r,i);
        while(l<=n&&pre[l]-pre[i-1]<L) l++;
        while(r<=n&&pre[r]-pre[i-1]<R) r++;
        r--;
        if(l>r) continue;
        if(pre[l]-pre[i-1]>=R||pre[l]-pre[i-1]<L) continue;
        if(pre[r]-pre[i-1]<L||pre[r]-pre[i-1]>=R) continue;
        res+=(ll)(r-l+1)*(ll)i+(ll)(r-l+1)*(l+r)/2;
    }
    return res;
}

int main(){

    int T;
    scanf("%d",&T);
    for(int i=0;i<35;i++){
        pw[i]=1ll<<i;
    }
    while(T--){
        scanf("%d",&n);
        pre[0]=0;
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            pre[i]=pre[i-1]+a[i];
        }
        ans=0;
        for(int i=1;i<35;i++){
            ans+=(ll)i*get_sum(pw[i-1],pw[i]);
        }
        ans+=get_sum(0,1);
        printf("%lld\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值