题目链接

循环求和

题目描述

牛牛定义了一个新的自然数序列:

即,对于一个自然数 ,如果它是奇数,其值为 ;如果它是偶数,其值为

现在需要计算这个序列第 项到第 项之间的和。

解题思路

这是一个求解特殊序列区间和的问题。由于区间的左右端点 的值可能非常大(可达 ),直接循环累加会导致超时。因此,我们需要寻找一种更高效的数学方法,通常是利用前缀和的思想。

1. 前缀和

区间 的和可以表示为 (从第 1 项到第 R 项的和) - (从第 1 项到第 L-1 项的和)

如果我们能用一个公式 快速计算出序列前 项的和,那么问题就迎刃而解了。答案就是

2. 推导前缀和公式

我们来观察一下前缀和 的规律:

可以发现一个非常清晰的模式:

  • 奇数时,

  • 偶数时,

这个公式可以通过将序列中的数两两配对来证明,例如 ,每一对的和都是

3. 最终算法

根据推导出的公式,我们可以设计一个函数 get_s(n),在 的时间内计算出前 项的和。

对于每个查询 ,我们只需计算 即可得到答案。

注意,输入的 很大,计算过程中应使用 64 位整型(如 C++ 的 long long)以避免溢出。

代码

#include <bits/stdc++.h>

using namespace std;

long long get_s(long long n) {
    if (n == 0) {
        return 0;
    }
    if (n % 2 == 1) {
        return (n + 1) / 2;
    } else {
        return -n / 2;
    }
}

void solve() {
    long long l, r;
    cin >> l >> r;
    long long ans = get_s(r) - get_s(l - 1);
    cout << ans << endl;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int t = sc.nextInt();
        while (t-- > 0) {
            solve(sc);
        }
    }

    public static long getS(long n) {
        if (n == 0) {
            return 0;
        }
        if (n % 2 == 1) {
            return (n + 1) / 2;
        } else {
            return -n / 2;
        }
    }
    
    public static void solve(Scanner sc) {
        long l = sc.nextLong();
        long r = sc.nextLong();
        long ans = getS(r) - getS(l - 1);
        System.out.println(ans);
    }
}
import sys

def get_s(n):
    if n == 0:
        return 0
    if n % 2 == 1:
        return (n + 1) // 2
    else:
        return -n // 2

def solve():
    l, r = map(int, sys.stdin.readline().split())
    ans = get_s(r) - get_s(l - 1)
    print(ans)

def main():
    try:
        t_str = sys.stdin.readline()
        if not t_str: return
        t = int(t_str)
        for _ in range(t):
            solve()
    except (IOError, ValueError):
        return

if __name__ == "__main__":
    main()

算法及复杂度

  • 算法:数学 / 前缀和

  • 时间复杂度:对于 组测试数据,每次查询的时间复杂度是 ,所以总时间复杂度为

  • 空间复杂度