题目链接

交替加减

题目描述

给定一个由 个整数构成的数组,进行如下操作:

  1. 在数组元素之间交替插入 +- 号(第一个是 +),形成一个表达式并计算,得到一个新数组。
  2. 对新数组重复此过程,但这次插入的符号序列的起始符号,与上一轮符号序列的末尾符号相反
  3. 重复以上步骤,直到数组只剩下一个数字。

求这个最终剩下的数字对 取模的结果。

解题思路

直接模拟 轮操作的复杂度为 ,无法通过本题。我们需要找到最终结果与初始数组之间的数学关系。最终结果可以表示为初始数组元素的线性组合:。我们的目标是求出系数

通过对不同 的推导,可以发现系数 的规律与组合数有关,并且具体形式取决于 的值。

  1. 是偶数时 ():

    • 最终结果可以表示为
    • 操作符 取决于 的奇偶性:
      • 是偶数 (即 ),则
      • 是奇数 (即 ),则
  2. 是奇数时 ():

    • 是偶数 (即 ),最终结果为 。只有偶数下标的元素有贡献。
    • 是奇数 (即 ),规律变得复杂。例如 时结果为 。在这种情况下,我们可以通过将 减一,并对数组进行一次变换,然后套用偶数情况的逻辑来求解。具体地,我们将数组 变换为 ,其中 ,然后对 个元素的 数组求解。由于 必为偶数,且新一轮计算的起始符号为 ,这会引导至 的情况。

算法步骤

  1. 根据 的值,选择对应的计算公式。
  2. 预计算组合数所需的阶乘和阶乘逆元。
  3. 根据公式,遍历数组 ,将每个元素乘以其对应的系数,累加求和。
  4. 所有计算都在模 下进行,注意处理负数取模。

代码

#include <iostream>
#include <vector>
#include <numeric>

using namespace std;
using LL = long long;

const int MOD = 1e9 + 7;

// 快速幂求a^b mod p
LL power(LL base, LL exp) {
    LL res = 1;
    base %= MOD;
    while (exp > 0) {
        if (exp % 2 == 1) res = (res * base) % MOD;
        base = (base * base) % MOD;
        exp /= 2;
    }
    return res;
}

// 费马小定理求逆元
LL inv(LL n) {
    return power(n, MOD - 2);
}

// 预处理阶乘和阶乘逆元
vector<LL> fact, invFact;
void precompute_factorials(int n) {
    if (n < 0) return;
    fact.resize(n + 1);
    invFact.resize(n + 1);
    fact[0] = 1;
    invFact[0] = 1;
    for (int i = 1; i <= n; i++) {
        fact[i] = (fact[i - 1] * i) % MOD;
        invFact[i] = inv(fact[i]);
    }
}

// 使用预处理的阶乘和逆元计算组合数
LL nCr_mod_p(int n, int r) {
    if (r < 0 || r > n) return 0;
    return (((fact[n] * invFact[r]) % MOD) * invFact[n - r]) % MOD;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int n;
    cin >> n;
    vector<LL> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }

    // n=1 是边界情况,直接输出
    if (n == 1) {
        cout << (a[0] % MOD + MOD) % MOD << "\n";
        return 0;
    }

    // 当 n % 4 == 3 时,进行一次变换,转化为 n % 4 == 2 的情况
    if (n % 4 == 3) {
        vector<LL> next_a(n - 1);
        for (int i = 0; i < n - 1; ++i) {
            if (i % 2 == 0) {
                next_a[i] = (a[i] + a[i+1]);
            } else {
                next_a[i] = (a[i] - a[i+1]);
            }
        }
        a = next_a;
        n--;
    }

    LL ans = 0;
    // n % 4 == 1 的情况
    if (n % 2 == 1) {
        int k = (n - 1) / 2;
        precompute_factorials(k);
        for (int j = 0; j <= k; ++j) {
            LL C = nCr_mod_p(k, j);
            ans = (ans + (C * a[2 * j]) % MOD) % MOD;
        }
    } else { // n 为偶数的情况
        int k = n / 2;
        precompute_factorials(k - 1);
        if (k % 2 == 1) { // n % 4 == 2 的情况
            for (int j = 0; j < k; ++j) {
                LL C = nCr_mod_p(k - 1, j);
                LL term = (a[2 * j] + a[2 * j + 1]);
                ans = (ans + (C * (term % MOD + MOD) % MOD)) % MOD;
            }
        } else { // n % 4 == 0 的情况
            for (int j = 0; j < k; ++j) {
                LL C = nCr_mod_p(k - 1, j);
                LL term = (a[2 * j] - a[2 * j + 1]);
                ans = (ans + (C * (term % MOD + MOD) % MOD)) % MOD;
            }
        }
    }

    cout << (ans + MOD) % MOD << "\n";

    return 0;
}
import java.util.Scanner;

public class Main {
    static final int MOD = 1_000_000_007;
    static long[] fact;
    static long[] invFact;

    // 快速幂求a^b mod p
    static long power(long base, long exp) {
        long res = 1;
        base %= MOD;
        while (exp > 0) {
            if (exp % 2 == 1) res = (res * base) % MOD;
            base = (base * base) % MOD;
            exp /= 2;
        }
        return res;
    }

    // 费马小定理求逆元
    static long inv(long n) {
        return power(n, MOD - 2);
    }

    // 预处理阶乘和阶乘逆元
    static void precomputeFactorials(int n) {
        if (n < 0) return;
        fact = new long[n + 1];
        invFact = new long[n + 1];
        fact[0] = 1;
        invFact[0] = 1;
        for (int i = 1; i <= n; i++) {
            fact[i] = (fact[i - 1] * i) % MOD;
            invFact[i] = inv(fact[i]);
        }
    }

    // 使用预处理的阶乘和逆元计算组合数
    static long nCr_mod_p(int n, int r) {
        if (r < 0 || r > n) return 0;
        return (((fact[n] * invFact[r]) % MOD) * invFact[n - r]) % MOD;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        long[] a = new long[n];
        for (int i = 0; i < n; i++) {
            a[i] = sc.nextLong();
        }

        // n=1 是边界情况,直接输出
        if (n == 1) {
            System.out.println((a[0] % MOD + MOD) % MOD);
            return;
        }

        // 当 n % 4 == 3 时,进行一次变换,转化为 n % 4 == 2 的情况
        if (n % 4 == 3) {
            long[] next_a = new long[n - 1];
            for (int i = 0; i < n - 1; i++) {
                if (i % 2 == 0) {
                    next_a[i] = a[i] + a[i+1];
                } else {
                    next_a[i] = a[i] - a[i+1];
                }
            }
            a = next_a;
            n--;
        }

        long ans = 0;
        // n % 4 == 1 的情况
        if (n % 2 == 1) {
            int k = (n - 1) / 2;
            precomputeFactorials(k);
            for (int j = 0; j <= k; ++j) {
                long C = nCr_mod_p(k, j);
                ans = (ans + (C * a[2 * j]) % MOD) % MOD;
            }
        } else { // n 为偶数的情况
            int k = n / 2;
            precomputeFactorials(k - 1);
            if (k % 2 == 1) { // n % 4 == 2 的情况
                 for (int j = 0; j < k; ++j) {
                    long C = nCr_mod_p(k - 1, j);
                    long term = (a[2 * j] + a[2 * j + 1]);
                    long term_mod = (term % MOD + MOD) % MOD;
                    ans = (ans + (C * term_mod) % MOD) % MOD;
                }
            } else { // n % 4 == 0 的情况
                for (int j = 0; j < k; ++j) {
                    long C = nCr_mod_p(k - 1, j);
                    long term = (a[2 * j] - a[2 * j + 1]);
                    long term_mod = (term % MOD + MOD) % MOD;
                    ans = (ans + (C * term_mod) % MOD) % MOD;
                }
            }
        }

        System.out.println((ans + MOD) % MOD);
    }
}
MOD = 10**9 + 7

# 预计算阶乘和阶乘逆元
MAX_N = 200000 # 根据题目n的最大范围设定
fact = [1] * (MAX_N + 1)
invFact = [1] * (MAX_N + 1)
for i in range(1, MAX_N + 1):
    fact[i] = (fact[i-1] * i) % MOD

# 使用费马小定理和递推计算阶乘逆元
invFact[MAX_N] = pow(fact[MAX_N], MOD - 2, MOD)
for i in range(MAX_N - 1, -1, -1):
    invFact[i] = (invFact[i+1] * (i+1)) % MOD

# 使用预处理的阶乘和逆元计算组合数
def nCr_mod_p(n, r):
    if r < 0 or r > n:
        return 0
    numerator = fact[n]
    denominator = (invFact[r] * invFact[n - r]) % MOD
    return (numerator * denominator) % MOD

def main():
    n = int(input())
    a = list(map(int, input().split()))

    # n=1 是边界情况,直接输出
    if n == 1:
        print((a[0] % MOD + MOD) % MOD)
        return

    # 当 n % 4 == 3 时,进行一次变换,转化为 n % 4 == 2 的情况
    if n % 4 == 3:
        next_a = [0] * (n - 1)
        for i in range(n - 1):
            if i % 2 == 0:
                next_a[i] = a[i] + a[i+1]
            else:
                next_a[i] = a[i] - a[i+1]
        a = next_a
        n -= 1
    
    ans = 0
    # n % 4 == 1 的情况
    if n % 2 == 1:
        k = (n - 1) // 2
        for j in range(k + 1):
            C = nCr_mod_p(k, j)
            ans = (ans + C * a[2 * j]) % MOD
    else: # n 为偶数的情况
        k = n // 2
        if k % 2 == 1: # n % 4 == 2 的情况
            for j in range(k):
                C = nCr_mod_p(k - 1, j)
                term = a[2 * j] + a[2 * j + 1]
                ans = (ans + C * term) % MOD
        else: # n % 4 == 0 的情况
            for j in range(k):
                C = nCr_mod_p(k - 1, j)
                term = a[2 * j] - a[2 * j + 1]
                ans = (ans + C * term) % MOD

    # 最终结果处理负数取模
    print((ans + MOD) % MOD)

if __name__ == "__main__":
    main()

算法及复杂度

  • 算法:数学、组合计数、分类讨论
  • 时间复杂度:。所有情况都只需要遍历一次数组,而组合数查询在 预处理后为
  • 空间复杂度:,用于存储阶乘和阶乘逆元数组。