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 ) ) ;
}
}
}