昨天比赛的一题,觉得蛮有趣的,当是不会写后面看来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; }