hdu5358

soda has an integer array a1,a2,…,ana1,a2,…,an. Let S(i,j)S(i,j) be the sum of ai,ai+1,…,ajai,ai+1,…,aj. Now soda wants to know the value below:

∑i=1n∑j=in(⌊log2S(i,j)⌋+1)×(i+j)∑i=1n∑j=in(⌊log2⁡S(i,j)⌋+1)×(i+j)


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

Input

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

The first line contains an integer nn (1≤n≤105)(1≤n≤105), the number of integers in the array. 
The next line contains nn integers a1,a2,…,ana1,a2,…,an (0≤ai≤105)(0≤ai≤105).

Output

For each test case, output the value.

Sample Input

1
2
1 1

Sample Output

12

题意:
 

题意:给你一个整数序列a1,a2,a3,…,an,要求求出

的值,S(i,j)表示ai+ai+1+ai+2+…+aj

解题报告:观察式子可以知道

的值是x的二进制表示的位数。

比如x=5,它的二进制表示是101,将x=5代入上式,可得3,正是101的位数

另外题目又规定n的大小以及ai的大小都是100000以内的,所以,二进制位数最多34位,即

又因为该题的时间复杂度至多为O(nlogn),所以我们只需枚举位数k,再利用尺取法求出对应的i、j值即可。尺取法的时间复杂度是O(n)的。不知道尺取法的可以参考一下网址“算法初步——尺取法“,最好下载来看,毕竟ppt里有动画,看着比较直观。本来想要弄个学习笔记的,但是貌似CSDN不支持动画。

每对应一个i值,利用尺取法求出满足条件的j值的范围(a<=j<=b),则

i+j=(i+a)+(i+a+1)+(i+a+2)+…+(i+b)=(b-a+1)*i+(a+b)*(b-a+1)/2

换句话说,枚举位数i:1~35。对于每一位i找到区间[x,y],使得S(x,y)的二进制表示的位数等于i,此时的值为i*(x+y)。那么对于每一个i,怎么找出所有符合条件的区间[x,y]?1~n枚举起点x,那么y会在一段范围[l,r]内满足条件。下次x变成x+1,即起点x向右移位,那么现在要找的y的区间为[l',r'],l'不至于小于l同样r'不至于小于r。这样可以用O(n)的复杂度找出所有区间使得区间和的二进制表示的位数为枚举的i。

以下是AC代码 有什么不懂的地方可以提出来 

需要指出的一点是该方法仅用G++提交才能AC,若C++仍为TLE,据说是因为G++的输入输出比较快

代码:
 

#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;
const int N = 100005;
ll l[50],r[50],s[N],ans,a,b,num;
int main()
{
    int t,i,j,n,x;
	for(i=1;i<34;i++)
	{
		l[i]=(1ll<<i);
		r[i]=((1ll<<(i+1))-1);
	}
	l[0]=0;r[0]=1;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(i=1;i<=n;i++)
		{
			scanf("%d",&x);
			s[i]=s[i-1]+x;
		}
		ans=0;
		for(i=1;i<35;i++)
		{
			if(s[n]<l[i-1])
				break;
			a=1,b=num=0;
			for(j=1;j<=n;j++)
			{
				a=(a>j?a:j);
				while(a<=n&&s[a]-s[j-1]<l[i-1])
					a++;
				b=(b>a-1?b:a-1);
				while(b+1<=n&&s[b+1]-s[j-1]>=l[i-1]&&s[b+1]-s[j-1]<=r[i-1])
					b++;
				if(b>=a)
					num+=(b-a+1)*j+(b+a)*(b-a+1)/2;
			}
			ans+=num*i;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40859951/article/details/87557417