题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6304
题目大意:求Meta-Fibonacci的前缀和。
谜之求和,打表得到前几项发现是,1 1 2 2 3 4 4 4 5 6 6 7 8 8 8 8 9.......明显除了1之外,其他数字出现的次数跟它当中含有2的幂次有关,比如奇数中不含有2,所以只出现1次,而2=2^1,6=2^1*3......,出现了2次,4=2^2出现了3次。所以借队友的脑洞想到了如果是1 1 2 2......x,其中x是最后一次出现,那么这个序列对应有x!中含有2的个数+1+x项,然后我又推出了如果知道这个序列末尾是x,那么对应的前缀和为:
1+2+……+x+2*(1+2+……+x/2)+4*(1+2+……+x/4)+.......+2^k*(1+2+......x/2^k)。
其中k比x小的2的最高幂次。
若干个等差数列。
于是想到二分求x对应的项数,然后求和即可,但是比赛的时候2的逆元忘了打表,一直tle,赛后队友发现了这个问题,一下就过了......很多大佬说卡二分,我们也证实了确实没卡二分,就是自己太菜,这种细节都能忘了。
因为看dalao们该这个题改的挺痛苦的,我们就能优化的地方都优化了,输入挂,位运算,结果发现是卡了常数,当了一回小丑……
ac代码:
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#define mo 1000000007
using namespace std;
typedef long long ll;
long long acm;
inline ll read()
{
register ll c = getchar(),fg = 1,sum = 0;
while ( c > '9' || c < '0' )
{
if( c == '-' ) fg = -1 ;
c = getchar() ;
}
while ( c <= '9' && c >= '0' )
{
sum = sum * 10 + c - '0' ;
c = getchar() ;
}
return fg * sum ;
}
int a [ 1010 ], sum [ 1010 ] ;
long long getnum ( long long n ) {
long long num = 0 ;
long long nn = n ;
while ( nn ) {
num += nn / 2 ;
nn >>= 1 ;
}
return num + n + 1 ;
}//n!中2出现的次数
long long qpow ( long long a , long long b )
{
long long ans = 1 ;
a = a % mo;
while ( b ){
if ( b & 1 ) ans = ans * a % mo ;
a = a * a % mo ;
b >>= 1 ;
}
return ans;
}
long long getb ( long long x )
{
x=x%mo;
return ( x + ( ( x * ( x - 1 ) ) %mo * acm ) + mo ) % mo;
}//等差数列求和
long long getans ( long long n ) {
long long ans = 0 ;
long long temp = 1 ;
long long fun = 2 ;
while ( n ) {
ans = ( ans + ( temp % mo ) * ( getb ( n ) % mo ) ) % mo;
n /= fun ;
temp <<= 1 ;
}
return ( ans + 1 + mo ) % mo;
}
long long solve ( long long n )
{
long long l = ( n / 2 ) - 49 , r = ( n / 2 ) + 49 ;
long long fun , mid , fuck , ans ;
while ( l <= r )
{
mid = ( l + r ) >> 1 ;
if ( getnum ( mid ) <= n )
{
if ( getnum( mid + 1 ) > n )
{
fun = mid ;
break ;
}
else
{
l = mid ;
}
}
else
{
r = mid ;
}
}
fuck = getnum ( fun ) ;
ans = ( getans ( fun ) + ( ( ( n - fuck ) % mo ) * ( ( fun + 1 ) % mo ) ) % mo + mo ) % mo ;
return ans ;
}
int main(){
long long n;
int t;
a [ 1 ] = 1 ;
a [ 2 ] = 1 ;
sum [ 1 ] = 1 ;
sum [ 2 ] = 2 ;
for(int i = 3 ; i <= 1000 ; i++ ) {
a [ i ] = ( a [ i - a [ i - 1 ] ] + a [ i - 1 - a [ i - 2 ] ] ) ;
sum [ i ] = ( sum [ i - 1 ] + a [ i ] ) ;
}
n =2 ;
acm = qpow ( n , mo - 2 ) ;
scanf ( "%d" , &t ) ;
while ( t-- ) {
n = read() ;
if (n <= 1000 ) {
printf ( "%d\n", sum [ n ] ) ;
}
else
{
printf ( "%lld\n" , solve ( n ) ) ;
}
}
}