2018年hdu多校1007 Chiaki Sequence Revisited(谜之Meta-Fibonacci,二分+求n!中a出现的次数)

  题目链接: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 ) ) ;
        }
    }
}
 

猜你喜欢

转载自blog.csdn.net/m0_37772713/article/details/81181265