码迷,mamicode.com
首页 > 其他好文 > 详细

hdu 5358 First One (尺取法)

时间:2015-08-08 00:00:31      阅读:323      评论:0      收藏:0      [点我收藏+]

标签:多校联合训练   尺取法   

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5358

题意:技术分享

分析:首先要知道[log2(x)]+1代表x的位数,而且根据题意不会超过35,那么枚举位数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。总的时间复杂度为35*N。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int maxn = 1e5+6;
LL pow2l[50],pow2r[50],s[maxn];
int main()
{
	for(int i=1;i<=40;i++)
	{
		pow2l[i]=(1ll<<i);
		pow2r[i]=((1ll<<(i+1))-1);
	}
	pow2l[0]=0;
	pow2r[0]=1;
	int ncase,i,j,n,x;
	scanf("%d",&ncase);
	while(ncase--)
	{
		scanf("%d",&n);
		for(i=1;i<=n;i++)
		{
			scanf("%d",&x);
			s[i]=s[i-1]+x;
		}
		LL ans=0;
		for(i=1;i<=35;i++)
		{
			if(s[n]<pow2l[i-1])
				break;
			LL l=1,r=0,temp=0;
			for(j=1;j<=n;j++)
			{
				l=(l>j?l:j); 
				while(l<=n && s[l]-s[j-1]<pow2l[i-1])
					l++;
				r=(r>l-1?r:l-1);
				while(r+1<=n && s[r+1]-s[j-1]>=pow2l[i-1] && s[r+1]-s[j-1]<=pow2r[i-1])
					r++;
				if(r>=l)
					temp+=(r-l+1)*j+(r+l)*(r-l+1)/2;
			}
			ans+=temp*i;
		}
		printf("%I64d\n",ans);
	}
	return 0;
}


hdu 5358 First One (尺取法)

标签:多校联合训练   尺取法   

原文地址:http://blog.csdn.net/w20810/article/details/47346349

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!