hdu 5358 First One (2015多校第六場第6題)尺取法枚舉區間和

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=5358

題意:給你一個串數和一個公式,求這個公式的答案。

思路:直接暴力一定會超時的,而且這道題很卡時間,交C++的都TLE了。怎麼優化才能不超時,於是就有了尺取法,其實就相當於兩個分別指向區間左右指針不斷更新區間內容的過程。

尺取法(two point)就是兩個指針表示區間[l,r]的開始與結束然後根據題目來將端點移動,是一種十分有效的做法,適合連續區間的問題。

尺取法(two point)基本過程分爲4步:
    1.初始化左右端點
    2.不斷擴大右端點,直到滿足條件
    3.如果第二步中無法滿足條件,則終止,否則更新結果
    4.將左端點擴大1,然後回到第二步


代碼:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>

using namespace std;
#define N 100005
#define LL __int64

int a[N];
LL sum[N];

int getM(LL a)
{
    return !a?1:(int)log2(a*1.0)+1;
}

int main()
{
    int T,n,i,j,m;
    LL ans,pl,pr,p;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        ans=sum[0]=0;
        for(i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }
        m=getM(sum[n]);
        for(i=1; i<=m; i++)
        {
            int l=1,r=0;
             if(i==1)
                pl=0;
            else
                pl=1LL<<(i-1);
            pr=(1LL<<i);
            for(j=1; j<=n; j++)
            {
                l=max(j,l);
                while(l<=n&&sum[l]-sum[j-1]<pl)
                    l++;
                r=max(l-1,r);
                while( (r+1<=n) && (sum[r+1]-sum[j-1]>=pl) && (sum[r+1]-sum[j-1]<pr) )
                    r++;
                if(l<=r)
                    ans+=(LL)(l+r)*(r-l+1)/2*i+(LL)j*(r-l+1)*i;
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}


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