HDU 6304 Chiaki Sequence Revisited(二分+找规律)

题目链接

Chiaki Sequence Revisited

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2106    Accepted Submission(s): 585


 

Problem Description

Chiaki is interested in an infinite sequence a1,a2,a3,..., which is defined as follows: 

an={1an−an−1+an−1−an−2n=1,2n≥3


Chiaki would like to know the sum of the first n terms of the sequence, i.e. ∑i=1nai. As this number may be very large, Chiaki is only interested in its remainder modulo (109+7).

Input

There are multiple test cases. The first line of input contains an integer T (1≤T≤105), indicating the number of test cases. For each test case:
The first line contains an integer n (1≤n≤1018).

Output

For each test case, output an integer denoting the answer.

Sample Input

10

1

2

3

4

5

6

7

8

9

10

Sample Output

1

2

4

6

9

13

17

21

26

32

 题意:

给你一个序列,序列的公式在上面,然后输入一个n,让你输出a[1..n]的和,即他的前缀和

解析:

这种题一开始都是打表找规律,队友找到的规律是a[i]的值是按规律递增的,并且每一个值出现的次数都是他分解成k=2^w*p(w是该数最大的能被2整除的最大幂数)后,w+1次。譬如说12=2^2*4,所以12这个数会出现3次并且是连续出现的,即有3项a[i]=12(i=l,l+1,l+2),l未知。

把他们列出来就可以找到规律了。

1 3 5 7 9 .......     出现1次的差为2的等差数列

2 6 10 14 18.....   出现2次的差为4的等差数列

4 12 20 28 36 .....  出现3次的差为8的等差数列

8 24 40 56 72 ....  出现4次的差为16的等差数列

........

这样我们只要找到出现1次的等差数列的长度、出现2次的等差数列的长度、出现3次的等差数列的长度、......

我们就可以得到前缀和了。

那么我们首先就该知道a[n]这个数的值,这样才能求出上面各个等差数列的长度。

所以我们就二分它,因为你再观察一下这个数列就可以发现,这个每一个a[i]的值一定是在[i/2,i]之间的。

我们就二分它,那么如何验证。

首先知道一个数的值12=4*3,我们其实就可以得到各个等差数列的长度了。

1*12   ....(1,3,5,7,9,11)出现1次的等差数列长度为6

2*6 ....(2*1,2*3,2*5) 出现2次的等差数列长度为3

4*3....(4*1,4*3)  出现3次的等差数列长度为2

8*1.5 ....(8*1)  出现4次的等差数列长度为1

这样各个等差数列的长度就得到了。

那么验证的时候我们只要通过我们每次二分出来的值,来算出上述的长度。

并且这里需要注意因为n的范围是1e18,所以当最后得到a[n]的值计算前缀和的时候,作乘法的两个乘数都是需要模的,不然会爆long long !!!

ans=(tmp%MOD*(n%MOD))%MOD,这样子。

#include <cstdio>
#include <cstring>


typedef long long ll;
const int MOD = 1e9+7;

inline void solve(ll x,ll &a,ll &b)
{
    ll y=x;
    ll res=y/2+y%2;
    ll cnt=1;
    while(y%2==0)
    {
        cnt++;
        y=y>>1;
        res+=(y/2+y%2)*cnt;
    }
    ll tmp=cnt;
    while(y)
    {
        cnt++;
        y=y>>1;
        res+=(y/2+y%2)*cnt;
    }
    a=res-tmp+2;
    b=res+1;
    
}

inline ll cal(ll y,ll x)
{
    ll pre=y;
    ll res=y/2+y%2;
    ll ans=(res%MOD+(res%MOD*((res-1)%MOD))%MOD+1)%MOD;
    ll cnt=1;
    ll t2=1;
    ll pp,tmp;
    while(y%2==0)
    {
        t2=t2*2%MOD;
        cnt++;
        y=y>>1;
        if(y%2==0)
        {
            pp=(y/2+y%2);
            tmp=(((pp%MOD)*(t2%MOD))%MOD+((pp%MOD*((pp-1)%MOD))%MOD*(t2%MOD))%MOD)%MOD;
            tmp=((tmp%MOD)*(cnt%MOD))%MOD;
            
        }
        else
        {
            pp=(y/2+y%2)-1;
            tmp=((pp%MOD*(t2%MOD))%MOD+((pp%MOD*((pp-1)%MOD))%MOD*(t2%MOD))%MOD)%MOD;
            tmp=(tmp%MOD*(cnt%MOD))%MOD;
            tmp=(tmp+(pre%MOD*(x%MOD))%MOD)%MOD;
        }
        ans=(ans+tmp)%MOD;
    }
    while(y)
    {
        t2=t2*2%MOD;
        cnt++;
        y=y>>1;
        pp=(y/2+y%2);
        tmp=((pp%MOD*(t2%MOD))%MOD+((pp%MOD*((pp-1)%MOD))%MOD*(t2%MOD))%MOD)%MOD;
        tmp=((tmp%MOD)*(cnt%MOD))%MOD;
        ans=(ans+tmp)%MOD;
        
    }
    return ans;
}


ll bin_search(ll x)
{
    ll l,r;
    
    l=x/2;r=x;
    while(l<r)
    {
        ll mid=(l+r)>>1;
        ll a,b;
        solve(mid,a,b);
        if(b<x)
        {
            l=mid+1;
        }
        else if(a>x)
        {
            r=mid;
        }
        else
        {
            return cal(mid,x-a+1);
            break;
        }
    }
    /*ll a,b;
    solve(l,a,b);
    return cal(l,x-a+1);*/
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ll x;
        scanf("%lld",&x);
        if(x==1) printf("1\n");
        else if(x==2) printf("2\n");
        else
        {
           printf("%lld\n",bin_search(x));
        }
        
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37025443/article/details/81213501
今日推荐