HDU 6304 2018 HDU多校第一场 Chiaki Sequence Revisited(二分+倍增规律)

Chiaki Sequence Revisited

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

Problem Description

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

                                                         a_n=\begin{cases}1 & n = 1,2 \\ a_{n - a_{n-1}} + a_{n-1 - a_{n-2}} & n \ge 3\end{cases}

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 (10^9+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

Source

2018 Multi-University Training Contest 1

大致题意:告诉你数列的递推公式,让你求和……

首先,这种下标上面有前几项的数列,基本上不要去想用矩阵快速幂解决。还是想想规律吧……网上很多什么lowbit的,其实感觉有点误导,毕竟这个lowbit不是树状数组的lowbit,而是重新改编定义的lowbit。规律的话,首先列出前几项:

1,1,2,2,3,4,4,4,5,6,6,7,8,8,8,8,9,10,10,11,12,12,12,13,14,14,15,16,16,16,16,16,17……

已经写出来了这么多了……我们可以发现,如果不看第一个1,1,3,5,7,9,11,13,15,17都只出现了一次,2,6,10,14都只出现了两次,4,12,20,28都只出现了3次,8,24,40,56都只出现了4次……也就是说二进制下末位的0的个数加一对应了这个数字出现的次数,而且出现次数相同的数字构成等差数列,第i个等差数列的公差是2^i。如果我们能够确定出现的最大数字,那么我们就能够利用等差数列的求和公式计算出总共的和。

于是,我们考虑二分这个最大的数字。有了最大的数字后,同样的,利用上面发现的规律计算总共出现了多少个数字。经过观察可以发现这个总数其实是 \sum \frac{mid+2^{i-1}}{2^i}*i ,log倍增上去就可以求和,之后与n比较即可知道出现的最大数字。经过查资料,我们还可以发现一个更简单的系统自带函数,这个总数其实就是2*mid - __builtin_popcount(mid),如此O(1)求出和,否则用O(log)的方式还要实现缩小二分范围。

知道最大值之后,就是等差数列就和,首项加末项乘以项数除以二。设t为每个数列的数字个数,n为出现的最大数字,i为出现次数,那么对于一个公差为2^i的等差数列,它的和就是i*2^{i-1}*t^2,这个t为\frac{n+2^{i-1}}{2^i}。最后还会剩下一些位置空的,就用n+1来填满即可。具体见代码:

#include<bits/stdc++.h>
#define LL long long
#define ULL long long

using namespace std;

const int mod = 1e9+7;

int T;
LL n;

int cnt(LL x)
{
    LL ret=0;
    while(x)
    {
        x-=x&-x;
        ret++;
    }
    return ret;
}

LL sum(LL x)
{
    return 2*x-cnt(x);
}

LL power(int x)
{
    return (1LL<<x);
}

LL sqr(LL x)
{
    return x*x%mod;
}

LL calc(LL ans,LL n)
{
    LL m=n,ret=0;n=ans;
    for(LL i=1;i<62;i++)
    {
        LL tmp=(n+power(i-1))/power(i);
        ret+=power(i-1)%mod*sqr(tmp%mod)%mod*i%mod;
    }
    ret+=((m-sum(ans)-1)%mod)*((ans+1)%mod)%mod;
    if (ret>=mod) ret-=mod;
    return ret;
}
int main ()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%I64d",&n);
        LL l=n/2-100,r=n/2+100,mid,ans=0;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(sum(mid)<n) ans=mid,l=mid+1;
                        else r=mid-1;
        }
        printf("%I64d\n",(calc(ans,n)+1)%mod);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/81192093
今日推荐