zoj 3293

昨天比赛的一题,觉得蛮有趣的,当是不会写后面看来yuan神的题解才知道怎么写,主要是规律感觉好难找。

先说题意:给你一个数列满足3个条件:1 全部为正数,2 非递减,3 满足s = 2a * b (s> 0, b % 2 != 0)保证数列中s只出现a次。S = {2, 4, 4, 6, 8, 8, 8, 10, ...};

首先这个数列很好写出来,因为它肯定包含所有的偶数,只需要知道它出现几次就好了,只需要将这个偶数化为 2a * b

的形式就可以知道出现的次数a是多少了。我当时只能列出这个数列,对规律无从下手,yuan神给出的这个规律很重要就是第2^k-1项就是2^k结束,这样帮我们找到了一个方法就是用数组dp[k]统计前2^k-1项的和,以后就只要找到那一项在哪一段范围就好了。然而这点规律还是不足的,因为前n项的和如果每次都要化成2a * b肯定要超时,这里是否存在规律呢?答案是肯定的。先来观察数列S{2,4,4,6,8,8,8,10,12,12,14,16,16,16,16,18......},这里面有一个显然的东西是dp[k]=dp[k-1]+?,先考虑dp[2]=dp[1]+4+4;dp[3]=dp[2]+6+8+8+8,dp[4]=d[3]+10+12+12+14+16+16+16+16这些数有什么规律呢?以dp[k]为例,显然这些数必然是大于2^(k-1)的偶数那就分别是2^(k-1)+2,2^(k-1)+4,2^(k-1)+6,2^(k-1)+8......2^(k-1)+2^(k-2),2^(k-1)+2^(k-1).那么出现多少次呢?将显然决定于后面那个小的数2^(k-1)+2决定于2,2^(k-1)+6决定于6.这是你会发现和前面的数列惊人的一致,2,4,4,6,8,8,8,10,12,12,14,16,16,16,16,18......也就是后面的每一项就是2^(k-1)+2,2^(k-1)+4,2^(k-1)+4,2^(k-1)+6,2^(k-1)+8,2^(k-1)+8,2^(k-1)+8,2^(k-1)+10......2^(k-1)+2^(k-2),2^(k-1)+2^(k-1),然后就只要知道项数统计一下是2^k-1项,但是最后会多一项注意加进去。有了这些规律,你区间统计也不用怕了,就是个递归搞定了。果断1A。

Run ID Submit Time Judge Status Problem ID Language Run Time(ms) Run Memory(KB) User Name
2628594 2011-08-15 16:26:45 Accepted 3293 C++ 260 188 xym
代码

#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
long long dp[33];
void DP()
{
	dp[1]=2;
	for(int i=2;i<=32;i++)
	{
		dp[i]=dp[i-1]*2+((1LL<<i-1)-1)*((1LL<<i-1))+(1LL<<i);
	}
}
long long count(long long n)
{
	long long kn;
	for(kn=0;((1<<kn)-1)<=n;kn++);
	        kn--;
			if(((1LL<<kn)-1)==n)
				return dp[kn];
			else return dp[kn]+(1LL<<kn)*(n-(1LL<<kn)+1)+count(n-(1<<kn)+1);
}
int main()
{
	long long n,m;
	DP();
	while(scanf("%lld%lld",&n,&m)!=EOF)
	{
		printf("%lld\n",count(m)-count(n-1));
	}
	return 0;
}


你可能感兴趣的:(c,user)