hdu5358(尺取法)

这题若是按常规的思路肯定会超时,我们需要注意题目中给出的每一个条件,注意有log2这个条件,s(i,j)在[2^(k-1),2^k)之间时 log2(s(i,j))下取整+1的值是k,枚举k从1到34 ,求出对应的s(i,j)在[2^(k-1),2^k)之间时i+j的和,求这个的话,可以枚举起始点从1到n,用两个指针不断后移就行
#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<cstring>
using namespace std;
typedef long long ll;
const int MAX=1e5+10;
const int M=35;
int T,n;
int a[MAX];
ll sum[MAX],pw[M];
ll get_sum(ll L,ll R){                 //计算[L,R)之间i+j的总和
    int l=1,r=0;
    ll SUM=0;
    for(int i=1;i<=n;i++){
        if(l<i) l=i;
        if(r<i-1) r=i-1;
        while(l<=n && sum[l]-sum[i-1]<L) l++; //计算第一个l,使得sum[i~l]>=L
        while(r+1<=n && sum[r+1]-sum[i-1]<R) r++; //计算第一个r,是的sum[i~r]<R
        if(l>r) continue;
        if(sum[l]-sum[i-1]<L || sum[l]-sum[i-1]>=R) continue;
        if(sum[r]-sum[i-1]<L || sum[r]-sum[i-1]>=R) continue;
        SUM += ((ll)(r-l+1)*i+(ll)(l+r)*(r-l+1)/2); //区间大小为r-l+1,里面有r-l+1个i,至于j,它的值为[l,r],所以总和为(l+r)*(r-l+1)/2
    }
    return SUM;
}
int main()
{
    ll SUM=0,mm,m;
    scanf("%d",&T);
    while(T--)
    {
        int t;
        SUM=0;
        memset(sum,0,sizeof(sum));
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",a+i);
            sum[i]=sum[i-1]+a[i];
        }
        for(int i=0;i<M;i++)
            pw[i]=pow(2,i);
        for(int i=1;i<=34;i++){
            SUM = SUM + (ll)i*get_sum(pw[i-1],pw[i]); //i=i-1+1,即题中的log2*S(i,j)+1
        }
        SUM += get_sum(0,1);
        printf("%I64d\n",SUM);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值