2020年 ICPC 亚洲区域赛(上海)G-Fibonacci

本文探讨了如何优化计算斐波那契数列中g(x,y)的算法,从O(n)提升到更高效的方法。首先介绍了基础的O(n^2)解决方案,然后提出分组求和技巧和等差数列公式,最终实现了一个O(1)或O(n/3)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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=1nj=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 ni 个数,因此就有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/3i/3个偶数,所以 k = n / 3 − i / 3 k=n/3-i/3 k=n/3i/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 1n109 ,我们 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 kn(3+3k)k/2

同时, k = n / 3 k = n/3 k=n/3


对于第二份的数,先假设有 p p p个,会执行 p p pans += (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 pn/3(p1)p/2


对于第三份的数,先假设有 q q q个,会执行 q q qans += (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 qn/3(q1)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;
}

所以有没有大佬可以说一下更简单的办法?

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值