前缀和(积)
板子题。
如果根据 次询问每一次都乘一次,复杂度为
肯定是超时的,那我们进行前缀乘积处理,若输入为
,我们把数组第 0 位初始化为 1:
| 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| 1 | 2 | 4 | 5 | 1 | 4 |
那么再使用一个前缀积数组,第 位表示
:
| 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| 1 | 2 | 8 | 40 | 40 | 160 |
这样对于每次查询输入的 ,直接输出
就是所求结果
模逆元
由于需要你对结果 取余求出,如果数字特别特别大,则需要边处理边求余,例如针对
| 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| 1 | 114514 | 1919810 | 12312312 | 99999999 | 23499432 |
那么前缀积数组即:
| 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| 1 | 114514 | 845120807 | 980638054 | 732915325 | 721033749 |
但是显然不能直接相除,这里就需要一个模逆元来处理:
其中 ,具体证明见 OI-Wiki。
关键是我们如何理解它,我们都知道除法运算本质上就是乘这个数的逆元,在有理数范围内定义乘法函数 ,那么单位元表示的是
,因为 1 与任何一个数
结果不变。如果若能使
,那么称
互为逆元,即
,那么定义在模运算的除法
等价于
,只需要计算出
的逆元
即可,那么根据费马小定理
然后使用快速幂即可求出在模意义下的逆元,复杂度为
。
AC Code
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
constexpr int p = 1e9 + 7;
i64 binpow(i64 a, i64 b, i64 p)
{
i64 res = 1;
while (b > 0)
{
if (b & 1)
res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
i64 inv(i64 a, i64 p) { return binpow(a, p - 2, p); }
int main(int argc, char *argv[])
{
int n, q;
cin >> n >> q;
vector<int> vi(n + 1);
vector<int> prefix_mul(n + 1);
vi[0] = 1;
prefix_mul[0] = 1;
for (int i = 1; i <= n; i++)
{
cin >> vi[i];
}
i64 mul = 1;
for (int i = 1; i <= n; i++)
{
mul *= vi[i];
mul %= p;
prefix_mul[i] = mul;
}
for (int i = 0; i < q; i++)
{
int x, y;
cin >> x >> y;
i64 res = prefix_mul[y] * inv(prefix_mul[x - 1], p);
res %= p;
cout << res << " ";
}
return 0;
}

京公网安备 11010502036488号