First One
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Problem Description
soda has an integer array . Let be the sum of . Now soda wants to know the value below:
Note: In this problem, you can consider as 0.
Input
There are multiple test cases. The first line of input contains an integer , indicating the number of test cases. For each test case:
The first line contains an integer , the number of integers in the array.
The next line contains integers .
Output
For each test case, output the value.
Sample Input
1 2 1 1
Sample Output
12
Source
2015 Multi-University Training Contest 6
题意:给你一个整数序列a1,a2,a3,…,an,要求求出
的值,S(i,j)表示ai+ai+1+ai+2+…+aj
分析:
关键点1:
题目又规定n的大小以及ai的大小都是100000以内的,所以,二进制位数最多34位(10^10对应的二进制位数)
关键点2:
所以,我们要做的就是枚举位数k,求出相应的x和y
每对应一个x值,利用尺取法求出满足条件的y值的范围(a<=y<=b),则
i+sum(j)=(i+a)+(i+a+1)+(i+a+2)+…+(i+b)=(b-a+1)*i+(a+b)*(b-a+1)/2
ans+=k*i+sum(j)
关键点3:
尺取法过程:
1~n枚举起点x,那么y会在一段范围[l,r]内满足条件。下次x变成x+1,即起点x向右移位,那么现在要找的y的区间为[l',r'],l'不至于小于l同样r'不至于小于r。这样可以用O(n)的复杂度找出所有区间使得区间和的二进制表示的位数为枚举的i。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <math.h>
#include <bitset>
#include <algorithm>
#include <climits>
using namespace std;
typedef long long ll;
ll a[100010];
ll sum[100010];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
}
ll ans=0;
for(ll i=1;i<=35;i++)//都要用long long
{
ll l=1,r=0;
ll minn=1ll<<(i-1),maxx=(1ll<<i)-1;
if(i==1)
minn=0;
for(ll x=1;x<=n;x++)
{
l=max(l,x);
while(l<=n&&sum[l]-sum[x-1]<minn) l++;
r=max(r,l-1); ///r=l-1是一个小技巧,可以判断他是否满足题目所给的条件
while(r+1<=n&&sum[r+1]-sum[x-1]<=maxx&&sum[r+1]-sum[x-1]>=minn) r++;
if(l>r) continue;
ans=ans+(x*(r-l+1)+(r+l)*(r-l+1)/2)*i;
}
}
printf("%lld\n", ans);
}
}