ICPC 亚洲区域赛(上海)
G-Fibonacci
题目
斐波那契数列为1,1,2,3,5,8,13,21,…
可以看到,这个数列有以下特点:
奇,奇,偶,奇,奇,偶…
当 x x x 与 y y y 相乘为偶数时, g ( x , y ) = 1 g(x, y) = 1 g(x,y)=1。
计算 ∑ i = 1 n ∑ j = i + 1 n g ( f i , f j ) \sum_{i=1}^n\sum_{j=i+1}^ng(f_i,f_j) ∑i=1n∑j=i+1ng(fi,fj)
第一步
当 x x x 或 y y y 中其中一个为偶数时,他们相乘也为偶数。
当
i
i
i 为偶数时,
j
j
j 从
i
+
1
i+1
i+1 到
n
n
n 与它相乘,得到的结果也为偶数。而
j
j
j 从
i
+
1
i+1
i+1 到
n
n
n ,一共有
n
−
i
n - i
n−i 个数,因此就有ans += n - i;
当 i i i 为奇数时,若 j j j 从 i + 1 i+1 i+1 到 n n n 中,有 k k k 个偶数,那么答案也会加 k k k 。
那么这个 k k k 怎么求呢?
每三个数中,就有一个数为偶数。在给定的 n n n 个数中,就有 n / 3 n/3 n/3 个数为偶数。
比如, n n n 等于 10 10 10 ,该斐波那契数列为 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 1,1,2,3,5,8,13,21,34,55 1,1,2,3,5,8,13,21,34,55 ,其中的偶数只有 2 , 8 , 34 2,8,34 2,8,34 ,个数刚好为 10 / 3 = 3 10/3 = 3 10/3=3
因此从第 1 1 1 到 i i i 个数中,一共有 i / 3 i/3 i/3 个偶数。从第 1 1 1 到 n n n 个数中,一共有 n / 3 n/3 n/3 个偶数。
那么,从第 i + 1 i+1 i+1 到 n n n 个数中,有 n / 3 − i / 3 n/3-i/3 n/3−i/3个偶数,所以 k = n / 3 − i / 3 k=n/3-i/3 k=n/3−i/3
即,当
i
i
i 为奇数时,有 ans += n/3 - i/3
;
然后,就可以写出这样的代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main() {
int n;
cin >> n;
int ans = 0;
for (int i = 1; i <= n; i++) {
if (i % 3 == 0) {
ans += (n - i);
} else {
ans += (n / 3 - i / 3);
}
}
cout << ans << endl;
return 0;
}
嗯,毫无意外的超时了。
题目给出的范围是 1 ≤ n ≤ 1 0 9 1 \leq n \leq 10^9 1≤n≤109 ,我们 O ( n ) O(n) O(n) 的算法当然会超时。
因此,就有了第二种解法。
第二步
我们依然以 n = 10 n = 10 n=10 为例,
把 i i i 从 1 1 1 到 n n n 分成三份。
第一份i = 3, 6, 9...
第二份i = 1, 4, 7, 10...
第三份i = 2, 5, 8...
对于第一份的数,都有i%3 == 0
,会执行 ans += (n - i)
的操作。有
k
k
k个 符合i%3 == 0
的数,就会执行
k
k
k 次。把它们加起来,就是(n - 3) + (n - 6) + (n - 9) + ... + (n - 3 * k)
根据等差数列求和公式 ( a 1 + a n ) ∗ n / 2 (a_1 + a_n) * n / 2 (a1+an)∗n/2 ,
可以简化为 k ∗ n − ( 3 + 3 ∗ k ) ∗ k / 2 k * n - (3 + 3 *k) * k / 2 k∗n−(3+3∗k)∗k/2 。
同时, k = n / 3 k = n/3 k=n/3
对于第二份的数,先假设有
p
p
p个,会执行
p
p
p次ans += (n/3 - i/3)
的操作。同样,把它们加起来,就是(n/3 - 1/3) + (n/3 - 4/3) + (n/3 - 7/3) + (n/3 - 10/3) + ... + (n/3 - (3 * p - 2)/3)
,
先简化一下,就是 (n/3 - 0) + (n/3 - 1) + (n/3 - 2) + .. + (n/3 - (p - 1))
根据等差数列求和公式,简化得
p ∗ n / 3 − ( p − 1 ) ∗ p / 2 p*n/3 - (p - 1) * p / 2 p∗n/3−(p−1)∗p/2
对于第三份的数,先假设有
q
q
q个,会执行
q
q
q次ans += (n/3 - i/3)
的操作。同样,把它们加起来,就是(n/3 - 2/3) + (n/3 - 5/3) + (n/3 - 8/3) +... + (n/3 - (3 * q - 1)/3)
先简化一下,就是(n/3 - 0) + (n/3 - 1) + (n/3 - 2) + ... + (n/3 - (q - 1))
根据等差数列求和公式,简化得
q ∗ n / 3 − ( q − 1 ) ∗ q / 2 q*n/3 - (q-1)*q/2 q∗n/3−(q−1)∗q/2
最后,把上面三条简化后的式子相加求和,就得到最终的结果。
实现代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main() {
int n;
cin >> n;
int ans = 0;
int k = n / 3;
ans += k * n - ((3 + 3 * k) * k) / 2;
int p = k, q = k;
if (n % 3 == 2) {
p++;
q++;
} else if (n % 3 == 1) {
p++;
}
ans += p * n - p * (p - 1) / 2;
ans += q * n - q * (q - 1) / 2;
cout << ans << endl;
return 0;
}
所以有没有大佬可以说一下更简单的办法?