First One
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 690 Accepted Submission(s): 205
Note: In this problem, you can consider
The first line contains an integer
The next line contains
1 2 1 1
12
解题思路:由于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 " #define pi (acos(-1.0)) #define eps (1e-6) #define inf (1<<28) #define sqr(x) (x) * (x) #define mod 1000000007 using 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 ",ans); } return 0; }