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:
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。如果我们能够确定出现的最大数字,那么我们就能够利用等差数列的求和公式计算出总共的和。
于是,我们考虑二分这个最大的数字。有了最大的数字后,同样的,利用上面发现的规律计算总共出现了多少个数字。经过观察可以发现这个总数其实是 ,log倍增上去就可以求和,之后与n比较即可知道出现的最大数字。经过查资料,我们还可以发现一个更简单的系统自带函数,这个总数其实就是2*mid - __builtin_popcount(mid),如此O(1)求出和,否则用O(log)的方式还要实现缩小二分范围。
知道最大值之后,就是等差数列就和,首项加末项乘以项数除以二。设t为每个数列的数字个数,n为出现的最大数字,i为出现次数,那么对于一个公差为的等差数列,它的和就是
,这个t为
。最后还会剩下一些位置空的,就用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;
}