2018 Multi-University Training Contest 1 G(Chiaki Sequence Revisited)

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
题意:就是求a[i]的前缀和

思路:先打表就能发现很明显的规律,就是一个数n如果有k个2做因子,那么将按顺序有k+1个n在表上出现(1是特殊)

比如1,1,2,2,3,4,4,4,5,6,6,7,8,8,8,8,9,10,10,11,,,,

那么设f(n)表示n!里以多少个2位因子,那么i=f(n)+n+1,那个1是首位的1,n是没一个数+1。其中公式求f(n)=(n/2)+(n/4)+(n/8)+...

此时发现如果一直i可以通过二分确定n。然后第二步,ans=1*(1+2+3+...n)+2*(1+2+3+...n/2)+4*(1+2+3+...n/4)。

其实可以发现1,3,5,7...这些数被加了一次,2*1,2*3,2*5...这些数被加了两次,一次类推就得出上述公式了。

这个规律我和队友合作推出来了,但是题目对时间卡的比较紧,没能当场优化出来很遗憾。下次努力。

#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 ;
}
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 = 1 , r =  n;
    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/snayf/article/details/81210719