HDU 5358 First One(枚举)

来源:互联网 发布:成都市软件行业协会 编辑:程序博客网 时间:2024/05/23 11:19

First One

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 690    Accepted Submission(s): 205


Problem Description
soda has an integer array a1,a2,,an. Let S(i,j) be the sum of ai,ai+1,,aj. Now soda wants to know the value below:
i=1nj=in(log2S(i,j)+1)×(i+j)

Note: In this problem, you can consider log20 as 0.
 

Input
There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:

The first line contains an integer n (1n105), the number of integers in the array.
The next line contains n integers a1,a2,,an (0ai105).
 

Output
For each test case, output the value.
 

Sample Input
121 1
 

Sample Output
12
 

Source
2015 Multi-University Training Contest 6
 


题目大意:对题目中的式子求结果。


解题思路:因为0<=ai<=10^5,0<n<=10^5,所以0<=S(i,j)<10^12<2^34,设k=log2S(i,j)⌋+1则1<=k<=34,那么我们

每次枚举k时,求解出所有符合条件的(i+j),求和即可。

而对于每一个k,求解(i+j)时,先预处理出s[i](s[i]=a1+……+ai,则sum(i,j)=s[j]-s[i-1]),那么接下来只需找到所有

满足2^(k-1)<=sum(i,j)<=2^k-1的(i+j)即可。

对于求(i+j),我们再次枚举i,对每一个i,求解出j的一个区间[l,r],使得对当前的i,有当l<=j<=r时,2^(k-1)

<=sum(i,j)<=2^k-1成立。那么对于当前的k,i,满足条件的i,j区间为[i,j](l<=j<=r),这些区间对应同一个k和同一个i,这些区间的(i+j)的总和为:i*(r-l+1)+(r+l)*(r-l+1)/2。

枚举完所有的k和i,将所有和累加。

对于求解区间[l,r],假设k=a,在枚举i=b时,得到j的区间[L1,R1],那么相同的k,在枚举i=b+1时,得到j的区间[L2,R2]

一定不在区间[L1,R1]的左边,简单的说就是L2>L1,R2>R1。因此查找l,r时可以减少范围。


代码如下:

#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <ctime>#include <iostream>#include <algorithm>#include <string>#include <vector>#include <deque>#include <list>#include <set>#include <map>#include <stack>#include <queue>#include <numeric>#include <iomanip>#include <bitset>#include <sstream>#include <fstream>#include <limits.h>#define debug "output for debug\n"#define pi (acos(-1.0))#define eps (1e-6)#define inf (1<<28)#define sqr(x) (x) * (x)#define mod 1000000007using namespace std;typedef long long ll;typedef unsigned long long ULL;ll fl[35]={0,0,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,2147483648,4294967296,8589934592};ll fr[35]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535,131071,262143,524287,1048575,2097151,4194303,8388607,16777215,33554431,67108863,134217727,268435455,536870911,1073741823,2147483647,4294967295,8589934591,17179869183};ll s[100005];int main(){    ll i,j,k,n,a,l,r,t;    scanf("%I64d",&t);    while(t--)    {        scanf("%I64d",&n);        for(i=1;i<=n;i++)        {            scanf("%I64d",&a);            s[i]=s[i-1]+a;        }        ll ans=0;        for(k=1;k<=34;k++)        {            l=1;            r=0;            //移位操作控制sum(i,j)的范围,也可以用数组            //fl= k==1?0:(1ll<<(k-1));fr=(1ll<<k)-1;            for(i=1;i<=n;i++)            {                l=max(i,l);                while(l<=n&&s[l]-s[i-1]<fl[k])//while(l<=n&&s[l]-s[i-1]<fl)                    l++;                r=max(l-1,r);                while(r+1<=n&&s[r+1]-s[i-1]>=fl[k]&&s[r+1]-s[i-1]<=fr[k])//while(r+1<=n&&s[r+1]-s[i-1]>=fl[k]&&s[r+1]-s[i-1]<=fr)                    r++;                if(l<=r)                    ans+=(i*(r-l+1)+(r+l)*(r-l+1)/2)*k;                    //ans+=(i+l+i+r)*(r-l+1)/2*k;            }        }        printf("%I64d\n",ans);    }    return 0;}

0 0