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

题目链接:传送门

题意:设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;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-17 11:19:56

HDU 5358 First One(枚举+尺举法)的相关文章

HDU 5358 First One 数学+尺取法

多校的题,摆明了数学题,但是没想出来,蠢爆了,之前算了半天的s[i][j]的和,其实是积.其实比赛的时候我连log(s[i][j])+1是s[i][j]的位数都没看出来,说出来都丢人. 知道了这个之后,就枚举二进制数的每一位,因为元素都是非负数,所以sum数组是非降的,这里用到了尺取法,之前也是听说过,应该是做过吧,不太记得了. 因为[2k-1,2k)的位数是k,枚举时,固定左端点,在sum数组找到最小的大于等于2k-1,最大的小于2k的点,这中间的点和左端点的s[i][j]就对于当前的k满足条

HDU 5358 First One(枚举)

这道题如果按照表达式一个个来算肯定超时,下午时候想了一个O(nlogn*logn)的算法,但是t了,因为这道题卡的非常紧几百个样例,必须nlogn的算法才可以ac 回到这道题,考虑log(sum(i,j))+1的特点,可以发现它的值域范围非常小,在1-34之间,那么我们可以考虑枚举log(sum(i,j)+1的值,记为k,然后统计(i+j)的和即可. 对于每一个k,找到所有满足2^(k-1)<=sum(i,j)<=2^k-1的(i+j), 那么我们考虑每个前缀i,找到这个前缀满足2^(k-1)

Hdu 5358 First One (尺取法+枚举)

题目链接: Hdu 5358 First One 题目描述: 数组a有n个元素,S[i,j]定义为a[i]+a[i+1]+.....+a[j],问:这个死东西等于多少? 解题思路: 二分肯定超,这个题目的时间卡的炒鸡严格,只有n*log(n)的复杂度才能过,n*log(n)^2都不可以的. 只需要枚举K,并且枚举区间左端i值,计算K的贡献值,然后遍历时候计算一下常数相加即可. 1 #include <cstdio> 2 #include <cstring> 3 #include &

HDU 1017 A Mathematical Curiosity【看懂题意+穷举法】

//2014.10.17    01:19 //题意: //先输入一个数N,然后分块输入,每块输入每次2个数,n,m,直到n,m同一时候为零时 //结束,当a和b满足题目要求时那么这对a和b就是一组 //注意: //每一块的输出中间有一个回车 A Mathematical Curiosity Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s)

HDU 1407 测试你是否和LTC水平一样高【穷举法】

/* 解题思路:暴力求解 难点详解:注意每个数都没有超过num 关键点:穷举法 解题人:lingnichong 解题时间:2014-08-28 10:56:15 解题体会:第一次使用goto语句,感觉goto语句还是有点实用的价值的 */ 测试你是否和LTC水平一样高 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 12878    A

C#跳转语句 迭代法 穷举法

一.跳转语句 break & continue break:跳出循环,终止此循环,不管下面还有多少次,全部跳过. string a=" ", for (int i=1;i<=10;I++) { if(i==5) { break; } a += i +",": } Console.WriteLine(a); 输出结果为 1,2,3,4,5 continue:终止此次循环,直接开始下次循环. string a=" ", for (int

什么叫穷举法?

穷举法的基本思想是根据题目的部分条件确定答案的大致范围,并在此范围内对所有可能的情况逐一验证,直到全部情况验证完毕.若某个情况验证符合题目的全部条件,则为本问题的一个解:若全部情况验证后都不符合题目的全部条件,则本题无解.穷举法也称为枚举法. 用穷举法解题时,就是按照某种方式列举问题答案的过程.针对问题的数据类型而言,常用的列举方法一有如下三种: (1)顺序列举 是指答案范围内的各种情况很容易与自然数对应甚至就是自然数,可以按自然数的变化顺序去列举. (2)排列列举 有时答案的数据形式是一组数的

HDU 5358 多校第6场 First One

First One Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 672    Accepted Submission(s): 193 Problem Description soda has an integer array . Let  be the sum of . Now soda wants to know the va

算法基础一 穷举法

/*穷举法*/ /*鸡兔同笼35头,94足,鸡兔各几只?*/ #include<stdio.h> const int Num = 35; const int Foots = 94; int main() { int cN;//鸡 int rN;//兔 for (cN = 0; cN <= 35; cN++) { rN = Num - cN; if (Foots == cN * 2 + rN * 4 ) { printf("鸡:%d,兔:%d\n",cN,rN); }