题目地址:Chiaki Sequence Revisited
题意:
给定一个无穷数列的递推式,求数列前n项的和。
思路:
通过观察发现,a[i]是从1开始到正无穷的递增数列,且每个正整数都至少会出现一次。通过找规律得,每个数num会出现k+1次,其中num = (2^k) * p1*p2*...*pn (pi均与2互质)。打表观察,1..n出现的次数以此为 1 2 1 3 1 2 1 4 1 2 1 3 1 2 1 5 ... 发现1...(2^(i-1)-1)出现的次数=2^(i-1)+1...(2^i-1)出现的次数,而2^(i-1)出现的次数+1=2^i出现的次数。由此可以推得,前2^i个数出现的次数和=前2^(i-1)个数出现的次数和*2+1。
然后,我们先求出x,x表示当a[n]及以前出现的且已经完全结束的最大整数(ex. 若a[n]=8,但a[n+1]=8,则x=7)。
即求满足 的最大x,其中num[i] = 正整数 i 出现的次数。
接下来计算1...x所有数字的和:(奇数肯定只出现1次)
1 3 5 7 9 ... 奇数,只出现1次
2 6 10 14 18 ... 除以2后为奇数的数,出现2次
4 12 20 28 36 ... 除以4后为奇数的数,出现3次
...
这些数的和可以通过等差数列求和得到。
但这只是答案的一部分,设tot=1..x所有数字出现的次数和,答案中还包括(n-tot-1)个x+1 。
PS:本题规律从a[2]开始找。
且这题用c++交一直T,用g++就过了......
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
ll n;
ll a[100]; //a[i]表示到数字2^i完全结束,所有数字的个数的总和
ll f[100]; // f[i]=2^i
int main()
{
int T;
scanf("%d",&T);
a[0]=1;
f[0]=1;
for(int i=1;i<=62;i++)
{
a[i]=a[i-1]*2+1;
f[i]=f[i-1]*2;
}
while(T--)
{
scanf("%lld",&n);
//计算x,x表示当a[n]及以前出现的且已经完全结束的最大整数
ll x,xx;
ll m=n;
x=0;
for(int i=62;i>=0;i--)
{
if(a[i]<=m)
{
m-=a[i];
x+=f[i];
}
}
x+=(m>0);
xx=x;
x=x-1;
ll ans=1;
ll tot=0;//记录已经计算了多少个数字
//计算到数字x全部结束后,产生的值
for(int i=1;i<=62;i++)
{
if(x==0) break;
if(x%2==0)
{
ll num=((x/2)%mod*i)%mod;
ans=(ans+(x/2)%mod*num%mod*(f[i-1]%mod)%mod)%mod;
tot+=(x/2)*i;
}
else
{
ll num=((x/2+1)%mod*i)%mod;
ans=(ans+((1+x)/2)%mod*num%mod*(f[i-1]%mod)%mod)%mod;
tot+=(x/2+1)*i;
}
x/=2;
}
//加上(n-tot-1)个xx
ans=(ans+(n-tot-1)%mod*(xx%mod)%mod)%mod;
printf("%lld\n",ans);
}
return 0;
}
/*
//暴力打表
ll a[150];
int main()
{
a[1]=a[2]=1;
printf("%d: %lld\n",1,a[1]);
printf("%d: %lld\n",2,a[2]);
for(int i=3;i<100;i++)
{
a[i]=a[i-a[i-1]]+a[i-1-a[i-2]];
printf("%d: %lld\n",i,a[i]);
}
return 0;
}
*/