杭电多校第一场 1007 Chiaki Sequence Revisited

1007 Chiaki Sequence Revisited

题意


给定 a n 的递推式,求 a n 的前缀和

打表

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

我们设数字i出现的次数 为 f ( n )
观察到规律,每一个数出现的次数是该数里含2的因子数的个数+1,转化成数学语言就是
如果有 n = 2 i t ( 2 t
那么有 f ( n ) = i + 1
唯一例外的就是1,排除掉这个就行了
那么从 1,…n 出现次数总和是多少呢
设为g(n)
g ( n ) = <munderover> i = 1 n </munderover> f ( i ) = n + n / 2 + n / 4 + n / 8 + . . . . . // 注意是整除
这个概念和阶乘尾零的求法很相似
例如 n = 8 的时候

n/ i 个数
n = 8 1 2 3 4 5 6 7 8 8个数字算一次
n/2 = 4 2 4 6 8 4个数字算二次
n/4 = 2 4 8 2个数字算三次
n/8 = 1 8 1个数字算四次
出现次数 1 2 1 3 1 2 1 4 15

好了我们知道这个规律之后就可以求得 a n 的值了,方法就是二分枚举 a n 的值,如果有 a n = m i d 则必有 g ( m i d 1 ) < n <= g ( m i d )

如果知道 a n 的值之后怎么求前缀和呢

求前缀和

<munderover> i = 1 i = n </munderover> a i = <munderover> i = 1 i = g ( m i d 1 ) </munderover> a i + <munderover> i = g ( m i d 1 ) + 1 n </munderover> a i = <munderover> i = 1 i = g ( m i d 1 ) </munderover> a i + ( n g ( m i d 1 ) ) m i d
所以问题转化成了 如何求 <munderover> i = 1 i = g ( m i d 1 ) </munderover> a i
我们又知道 每个数出现的次数

所以 <munderover> i = 1 i = g ( m i d 1 ) </munderover> a i = <munderover> i = 0 </munderover> m i d 1 2 i ( m i d 1 2 i + 1 ) 2 i
举例说明
1 2 3 4 5 6 7 8 求和一次 1 ( 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 ) = 1 8 ( 8 + 1 ) 2
2 4 6 8 求和一次是 2 ( 1 + 2 + 3 + 4 ) = 2 4 ( 4 + 1 ) 2
4 8 求和是 4 ( 1 + 2 = 4 2 ( 2 + 1 ) 2 ;

代码如下


#include <bits/stdc++.h>
#define mem(ar,num) memset(ar,num,sizeof(ar))
#define me(ar) memset(ar,0,sizeof(ar))
#define lowbit(x) (x&(-x))
#define Pb push_back
#define FI first
#define SE second
#define For(i,a,b) for(int i = a; i < b; ++i)
using namespace std;
typedef  long long LL;
typedef unsigned long long ULL;
const int    prime = 999983;
const int    INF = 0x7FFFFFFF;
const LL     INFF =0x7FFFFFFFFFFFFFFF;
const double pi = acos(-1.0);
const double inf = 1e18;
const double eps = 1e-6;
const LL     mod = 1e9 + 7;
int dr[2][4] = {1,-1,0,0,0,0,-1,1};
typedef pair<int,int> P;

LL n;
bool judge(LL mid,LL n){
    LL ans = 0;
    while(mid > 0)
       ans += mid,mid >>= 1;
    return ans >= n; 
}
const LL inv2 = 500000004;

const int maxn = 1e6;
LL a[maxn];
LL an[maxn];
LL  F(LL n){

    if(n < 200){
         return a[n];
     }
     n--;

     LL l = 1,r = n;
     while(r >= l){
         LL mid = l+((r-l)>>1);
         if(!judge(mid,n))
            l = mid+1;
         else 
            r = mid-1;
     }
     LL ans = 0;
     LL num = 0;
     while( r > 0)
       num += r, r >>=1;
     num = n-num;
     ans = (num%mod*(l%mod))%mod;
     l--;
     LL tmp = 1;
     while(1){
          LL t = l/tmp;
          if(t & 1)
             t = t%mod*(((t+1)/2)%mod)%mod;
          else 
             t = (t/2)%mod*((t+1)%mod)%mod;
         ans = (ans+(t*(tmp%mod)%mod))%mod; 
         tmp <<= 1;
         if(tmp > l)
           break;
     }
    return (ans+1)%mod;
} 
int main(void)
{  
  a[1] =a[2]= 1;
  for(int i = 3;i < maxn; ++i)
     a[i] = (a[i-a[i-1]]+a[i-1-a[i-2]])%mod;

  for(int i = 1;i < maxn;++i)
     a[i] = (a[i] +a[i-1])%mod;


  int T;
  scanf("%d",&T);
  for(int i = 1;i <= T; ++i){
     scanf("%lld",&n);
     LL ans = F(n);
     printf("%lld\n",ans);
  } 


   return 0;
}