题目链接
题目描述
给定一个长度为 的正整数序列
和
次查询。每次查询给出一对
(
),要求计算子数列
的乘积,并对模数
取模。
输入描述:
第一行输入两个整数 。
第二行输入
个正整数
。
接下来
行,每行输入两个整数
。
输出描述:
输出一行,包含 个用空格隔开的整数,表示每次查询的结果。
解题思路
本题是区间查询问题。对于每次查询,如果都遍历区间 计算乘积,总时间复杂度为
,会超时。我们需要更高效的方法。
前缀积 + 模逆元
我们可以借鉴前缀和的思想,使用 前缀积。
-
预处理前缀积数组: 我们创建一个数组
prefix_prod
,其中prefix_prod[i]
存储序列前个数的乘积对
取模的结果。
这个预处理过程的时间复杂度为
。
-
回答查询: 对于任意区间
的乘积,可以用前缀积数组计算得出:
在模算术中,除以一个数等于乘以它的 模逆元。因为模数
是一个质数,所以可以根据 费马小定理 求逆元:
因此,原式变为:
计算幂次的部分可以使用 快速幂 算法,其时间复杂度为
。
算法步骤总结:
- 定义模数
。
- 实现一个快速幂函数
power(base, exp)
,它在模下计算。
- 读入
和
。
- 创建大小为
的前缀积数组
prefix_prod
,并用的时间复杂度完成填充。
- 循环
次,对于每次查询
: a. 计算
prefix_prod[l-1]
的模逆元inv = power(prefix_prod[l-1], m - 2)
。 b. 计算结果ans = (prefix_prod[r] * inv) % m
。 c. 将ans
存入结果列表。 - 输出结果列表。
整体复杂度:
- 时间复杂度:
。
- 空间复杂度:
。
代码
#include <iostream>
#include <vector>
using namespace std;
const int MOD = 1e9 + 7;
long long power(long long base, long long exp) {
long long res = 1;
base %= MOD;
while (exp > 0) {
if (exp % 2 == 1) res = (res * base) % MOD;
base = (base * base) % MOD;
exp /= 2;
}
return res;
}
long long modInverse(long long n) {
return power(n, MOD - 2);
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n, q;
cin >> n >> q;
vector<long long> prefix_prod(n + 1);
prefix_prod[0] = 1;
for (int i = 1; i <= n; ++i) {
long long a;
cin >> a;
prefix_prod[i] = (prefix_prod[i - 1] * a) % MOD;
}
vector<long long> results;
for (int i = 0; i < q; ++i) {
int l, r;
cin >> l >> r;
long long numerator = prefix_prod[r];
long long denominator_inv = modInverse(prefix_prod[l - 1]);
results.push_back((numerator * denominator_inv) % MOD);
}
for (int i = 0; i < results.size(); ++i) {
cout << results[i] << (i == results.size() - 1 ? "" : " ");
}
cout << endl;
return 0;
}
import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;
public class Main {
static final int MOD = 1_000_000_007;
public 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;
}
public static long modInverse(long n) {
return power(n, MOD - 2);
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int q = sc.nextInt();
long[] prefixProd = new long[n + 1];
prefixProd[0] = 1;
for (int i = 1; i <= n; i++) {
long a = sc.nextLong();
prefixProd[i] = (prefixProd[i - 1] * a) % MOD;
}
List<Long> results = new ArrayList<>();
for (int i = 0; i < q; i++) {
int l = sc.nextInt();
int r = sc.nextInt();
long numerator = prefixProd[r];
long denominatorInv = modInverse(prefixProd[l - 1]);
results.add((numerator * denominatorInv) % MOD);
}
for (int i = 0; i < results.size(); i++) {
System.out.print(results.get(i) + (i == results.size() - 1 ? "" : " "));
}
System.out.println();
}
}
MOD = 10**9 + 7
def power(base, exp):
res = 1
base %= MOD
while exp > 0:
if exp % 2 == 1:
res = (res * base) % MOD
base = (base * base) % MOD
exp //= 2
return res
def mod_inverse(n):
return power(n, MOD - 2)
def solve():
n, q = map(int, input().split())
a = list(map(int, input().split()))
prefix_prod = [1] * (n + 1)
for i in range(n):
prefix_prod[i+1] = (prefix_prod[i] * a[i]) % MOD
results = []
for _ in range(q):
l, r = map(int, input().split())
numerator = prefix_prod[r]
denominator_inv = mod_inverse(prefix_prod[l - 1])
results.append((numerator * denominator_inv) % MOD)
print(*results)
solve()
算法及复杂度
- 算法:前缀积 + 模逆元 (使用快速幂)
- 时间复杂度:
,其中
是序列长度,
是查询次数,
是模数。
- 空间复杂度:
,用于存储前缀积数组。