HDU 5358 First One(枚举+尺举法)

来源:互联网 发布:武汉ui知乎 编辑:程序博客网 时间:2024/05/28 09:31

题目链接:传送门 

题意:设f(i,j)表示区间[i,j]内元素的和 ,定义 SUM(i,j) = [log2(f(i,j))+1]*(i+j)

求 sigma(sum (i,j)) ( 1<=i<=n,i<=j<=n )

分析: log2(f(i,j))表示f(i,j)转换为2进制的长度,然后我们经过分析log2(f(i,j))+1的值域

为[1,34]然后我们枚举log2(f(i,j))+1的值,例如我们枚举其值为k,对于一个k我们找到所有满足

条件的区间(i,j),这个条件的代数表达为 2^(k-1)<= f(i,j) <=2^k-1;

因此我们需要再枚举一个区间的左端点,对于一个给定的左端点,因为f(i,j)在给定i的情况下单调,

我们可以用尺举发求得一个区间[l,r],使得区间内的j (l<=j<=r)都瞒住sum(i,j)+1=k;

然后区间(i+j)的和可以表示为 i*(r-l+1) + (r+l)*(r-l+1)/2;


代码如下:

#include <iostream>#include <cstring>#include <algorithm>#include <cstdio>using namespace std;const int maxn = 1e5+10;typedef long long LL;LL sum[maxn];int main(){    int t,n;    scanf("%d",&t);    while(t--){        scanf("%d",&n);        sum[0]=0;        for(int i=1;i<=n;i++){            LL x;            scanf("%I64d",&x);            sum[i]=sum[i-1]+x;        }        LL ans = 0;        for(LL k = 1;k<=34;k++){            LL l=1,r=0;            LL lmax = 1LL<<(k-1),rmax=(1LL<<k)-1;            if(k==1) lmax = 0;            for(LL i=1;i<=n;i++){                l=max((LL)i,l);                while(l<=n&&sum[l]-sum[i-1]<lmax) l++;                r=max(l-1,r);                while(r+1<=n&&sum[r+1]-sum[i-1]<=rmax&&sum[r+1]-sum[i-1]>=lmax)r++;                if(l>r) continue;                ans=ans+(i*(r-l+1)+(r+l)*(r-l+1)/2)*k;            }        }        printf("%I64d\n",ans);    }    return 0;}


1 0
原创粉丝点击