@Chiaki Sequence Revisited@
@题目描述-English@
Chiaki is interested in an infinite sequence
a1,a2,a3,...
a
1
,
a
2
,
a
3
,
.
.
.
, which is defined as follows:
Chiaki would like to know the sum of the first n terms of the sequence, i.e. ∑ni=1ai ∑ i = 1 n a i . As this number may be very large, Chiaki is only interested in its remainder modulo ( 109+7 10 9 + 7 ).
Input
There are multiple test cases. The first line of input contains an integer T (1≤T≤10^5), indicating the number of test cases. For each test case:
The first line contains an integer n (1≤n≤10^18).
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 递推公式如上,求数组 a 的前缀和。
@分析 - 遇事不决先打表@
如题,数列 a 打表如下(`・ω・´):
a: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,…
目测好像有点儿规律的样子(`・ω・´)
记f[x]表示 x 连续出现多少次,继续来打波表(`・ω・´)
f:2,2,1,3,1,2,1,4,1,2,1,3,1,2,1,…
如果将f[1]减1,就可以发现 f 数组其实真的是有规律的(`・ω・´)
f 数组的前 1(2^1 - 1) 个:1
f 数组的前 3(2^2 - 1) 个:1,2,1
f 数组的前 7(2^3 - 1) 个:1,2,1,3,1,2,1
f 数组的前 15(2^4 - 1) 个:1,2,1,3,1,2,1,4,1,2,1,3,1,2,1
这样规律就非常明显了嘛(`・ω・´)
@分析 - 利用规律瞎求解@
首先考虑怎么表达“将f[1]减1”,其实这个是等价于“忽略a[1]”,我们可以将项数N-1,求解一遍前缀和,再让最后答案+1。
然后,记 s 为 f 数组的前缀和数组,我们找到数 x 使得 s[x] <= N 且 s[x+1] > N,则最后答案为 (∑xi=1i∗f[i])+(N−s[x])∗(x+1) ( ∑ i = 1 x i ∗ f [ i ] ) + ( N − s [ x ] ) ∗ ( x + 1 ) 。
考虑怎么找这样一个 x,因为 f 数组有着极好的自相似性,我们考虑用倍增法。记 cnt[i]=s[2i−1] c n t [ i ] = s [ 2 i − 1 ] ,则有 cnt[i]=cnt[i−1]∗2+i c n t [ i ] = c n t [ i − 1 ] ∗ 2 + i ,可以用类似于求 LCA 的方法求出 x :先找到最大的 p 满足 cnt[p] <= N,如果cnt[p] + (p+1) > N,则将 2^p - 1 累加进 x,跳出迭代;否则将 2^p 累加进 x ,将 N 减去 cnt[p] + (p+1) ,继续迭代操作。
再考虑怎么求解
∑xi=1i∗f[i]
∑
i
=
1
x
i
∗
f
[
i
]
,我们也可以利用 f 数组的自相似性来使用倍增法。
记
sum[x]=∑2x−1i=1i∗f[i]
s
u
m
[
x
]
=
∑
i
=
1
2
x
−
1
i
∗
f
[
i
]
。
因为
f[1...2x−1−1]=f[2x−1+1...2x−1]
f
[
1...2
x
−
1
−
1
]
=
f
[
2
x
−
1
+
1...2
x
−
1
]
(说白就是关于中间点对称)
有
sum[x]=(∑2x−1−1i=1i∗f[i])+(∑2x−1−1i=1(i+2x−1)∗f[i])+2x−1∗x
s
u
m
[
x
]
=
(
∑
i
=
1
2
x
−
1
−
1
i
∗
f
[
i
]
)
+
(
∑
i
=
1
2
x
−
1
−
1
(
i
+
2
x
−
1
)
∗
f
[
i
]
)
+
2
x
−
1
∗
x
即
sum[x]=sum[x−1]+2x−1∗x+(sum[x−1]+2x−1∗cnt[x−1])
s
u
m
[
x
]
=
s
u
m
[
x
−
1
]
+
2
x
−
1
∗
x
+
(
s
u
m
[
x
−
1
]
+
2
x
−
1
∗
c
n
t
[
x
−
1
]
)
就可以对 sum 数组进行递推
其他的和上面的方法相类似,也是先找到一个最大且小于等于 x 的数,分两类讨论,详细细节见代码。
@代码@
【我一开始连这道题题面都不知道,队友给我一个规律我就照着规律写了233】
【万能的队友】
#include<cstdio>
#include<cmath>
typedef long long ll;
const int MOD = int(1E9) + 7;
ll cnt[64], sum[64], pw2[64];
ll cnt_mod[64], pw2_mod[64];
void init() {
cnt[1] = 1; pw2[1] = 2; sum[1] = 1;
cnt_mod[1] = 1, pw2_mod[1] = 2;
for(int i=2;i<=60;i++) {
cnt[i] = cnt[i-1]*2 + i;//这里不能取模!
sum[i] = ((sum[i-1] + pw2_mod[i-1]*i%MOD)%MOD + (sum[i-1] + cnt_mod[i-1]*pw2_mod[i-1]%MOD)%MOD)%MOD;
pw2[i] = pw2[i-1] * 2;//这里也不能取模!
cnt_mod[i] = (cnt_mod[i-1]*2 + i) % MOD;
pw2_mod[i] = pw2_mod[i-1] * 2 % MOD;
}
}
ll solve(ll N) {
ll x = 0;
for(int i=60;i>=1;i--) {
if( cnt[i] <= N ) {
if( cnt[i] + (i+1) > N ) {
x += pw2[i] - 1;
N -= cnt[i];
break;
}
else {
x += pw2[i];
N -= (cnt[i] + (i + 1));
}
}
}
ll ret = 0, lst = 0, temp = x;
for(int i=60;i>=1;i--) {
if( pw2[i] - 1 <= temp ) {
if( pw2[i] > temp ) {
ret = (ret + sum[i] + cnt_mod[i]*lst%MOD)%MOD;
break;
}
else {
ret = (ret + sum[i] + cnt_mod[i]*lst%MOD)%MOD;
ret = (ret + (pw2_mod[i]+lst)%MOD*(i+1)%MOD)%MOD;
lst = (lst + pw2_mod[i]%MOD)%MOD;
temp -= pw2[i];
}
}
}
return (ret + N%MOD*(x+1)%MOD)%MOD;
}
int main() {
init(); int T;
scanf("%d", &T);
for(int i=1;i<=T;i++) {
ll N;
scanf("%lld", &N);
printf("%lld\n", (solve(N-1) + 1) % MOD);
}
}
@END@
就是这样,新的一天里,也请多多关照哦(ノω<。)ノ))☆.。~