Chiaki Sequence Revisited(hdu 6304 数列+二分)

题目地址:Chiaki Sequence Revisited

题意:

给定一个无穷数列的递推式,求数列前n项的和。

思路:

通过观察发现,a[i]是从1开始到正无穷的递增数列,且每个正整数都至少会出现一次。通过找规律得,每个数num会出现k+1次,其中num = (2^k) * p1*p2*...*pn (pi均与2互质)。打表观察,1..n出现的次数以此为 1 2 1 3 1 2 1 4 1 2 1 3 1 2 1 5 ... 发现1...(2^(i-1)-1)出现的次数=2^(i-1)+1...(2^i-1)出现的次数,而2^(i-1)出现的次数+1=2^i出现的次数。由此可以推得,前2^i个数出现的次数和=前2^(i-1)个数出现的次数和*2+1。

然后,我们先求出x,x表示当a[n]及以前出现的且已经完全结束的最大整数(ex. 若a[n]=8,但a[n+1]=8,则x=7)。

即求满足  \sum_{i=1}^{x} num[i] <= n 的最大x,其中num[i] = 正整数 i 出现的次数。

接下来计算1...x所有数字的和:(奇数肯定只出现1次)

1 3 5 7 9 ... 奇数,只出现1次 

2 6 10 14 18 ... 除以2后为奇数的数,出现2次

4 12 20 28 36 ... 除以4后为奇数的数,出现3次

...

这些数的和可以通过等差数列求和得到。

但这只是答案的一部分,设tot=1..x所有数字出现的次数和,答案中还包括(n-tot-1)个x+1

PS:本题规律从a[2]开始找。

且这题用c++交一直T,用g++就过了......

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

typedef long long ll;

const ll mod = 1e9+7;

ll n;
ll a[100]; //a[i]表示到数字2^i完全结束,所有数字的个数的总和
ll f[100]; // f[i]=2^i

int main()
{
    int T;
    scanf("%d",&T);
    a[0]=1;
    f[0]=1;
    for(int i=1;i<=62;i++)
    {
        a[i]=a[i-1]*2+1;
        f[i]=f[i-1]*2;
    }
    while(T--)
    {
        scanf("%lld",&n);

        //计算x,x表示当a[n]及以前出现的且已经完全结束的最大整数
        ll x,xx;
        ll m=n;
        x=0;
        for(int i=62;i>=0;i--)
        {
            if(a[i]<=m)
            {
                m-=a[i];
                x+=f[i];
            }
        }
        x+=(m>0);
        xx=x;
        x=x-1;
        ll ans=1;
        ll tot=0;//记录已经计算了多少个数字
        //计算到数字x全部结束后,产生的值
        for(int i=1;i<=62;i++)
        {
            if(x==0)    break;
            if(x%2==0)
            {
                ll num=((x/2)%mod*i)%mod;
                ans=(ans+(x/2)%mod*num%mod*(f[i-1]%mod)%mod)%mod;
                tot+=(x/2)*i;
            }
            else
            {
                ll num=((x/2+1)%mod*i)%mod;
                ans=(ans+((1+x)/2)%mod*num%mod*(f[i-1]%mod)%mod)%mod;
                tot+=(x/2+1)*i;
            }
            x/=2;
        }
        //加上(n-tot-1)个xx
        ans=(ans+(n-tot-1)%mod*(xx%mod)%mod)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}


/*
//暴力打表
ll a[150];
int main()
{
    a[1]=a[2]=1;
    printf("%d: %lld\n",1,a[1]);
    printf("%d: %lld\n",2,a[2]);
    for(int i=3;i<100;i++)
    {
        a[i]=a[i-a[i-1]]+a[i-1-a[i-2]];
        printf("%d: %lld\n",i,a[i]);
    }
    return 0;
}
*/

猜你喜欢

转载自blog.csdn.net/luyehao1/article/details/81184708
今日推荐