奇妙序列
难度:
标签:
题目描述
代码结果
运行时间: 436 ms, 内存: 53.9 MB
/*
* 题目思路:
* 与Java代码相同,只是这里使用Java Stream处理append操作。
* 我们还是延迟更新策略,在getIndex时计算实际值。
*/
import java.util.ArrayList;
import java.util.stream.IntStream;
public class Fancy {
private static final int MOD = 1_000_000_007;
private ArrayList<Integer> sequence;
private int add;
private int mul;
public Fancy() {
sequence = new ArrayList<>();
add = 0;
mul = 1;
}
public void append(int val) {
val = (int)((val - add + MOD) % MOD);
val = (int)((val * modInverse(mul, MOD)) % MOD);
sequence.add(val);
}
public void addAll(int inc) {
add = (int)((add + inc) % MOD);
}
public void multAll(int m) {
mul = (int)((mul * m) % MOD);
add = (int)((add * m) % MOD);
}
public int getIndex(int idx) {
if (idx >= sequence.size()) {
return -1;
}
return (int)(((long)sequence.get(idx) * mul + add) % MOD);
}
private int modInverse(int a, int m) {
return power(a, m - 2, m);
}
private int power(int x, int y, int p) {
int res = 1;
x = x % p;
while (y > 0) {
if ((y & 1) == 1)
res = (res * x) % p;
y = y >> 1;
x = (x * x) % p;
}
return res;
}
}
解释
方法:
这个题解利用线性变换的组合来避免对序列每个元素逐一进行操作,从而提高效率。每次添加或乘法操作时,我们不直接更新序列,而是记录当前操作的一次项系数和常数项,并将其与之前的操作合并。当调用getIndex时,我们使用保存的操作状态和反函数来计算具体位置的数值。这种方法把对整个序列的操作压缩为对两个参数(一次项系数和常数项)的操作,大大降低了处理时间。
时间复杂度:
O(1) per operation
空间复杂度:
O(n)
代码细节讲解
🦆
请问在`Fancy`类中,`k`和`b`数组分别用来存储什么信息?它们是如何反映序列操作的历史的?
▷🦆
为什么在`addAll`和`multAll`方法中更新`k`和`b`时需要取模运算?取模运算的目的是什么?
▷🦆
在`getIndex`方法中,为什么需要计算`one_over_k`并使用费马小定理?这里的费马小定理是如何应用的?
▷🦆
在`getIndex`方法中,如果`idx`指向的元素在多次操作后,如何确保最终结果的正确性?
▷