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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章