【LetMeFly】1622.奇妙序列:懒更新
力扣题目链接:https://leetcode.cn/problems/fancy-sequence/
请你实现三个 API append,addAll 和 multAll 来实现奇妙序列。
请实现 Fancy 类 :
Fancy()初始化一个空序列对象。void append(val)将整数val添加在序列末尾。void addAll(inc)将所有序列中的现有数值都增加inc。void multAll(m)将序列中的所有现有数值都乘以整数m。int getIndex(idx)得到下标为idx处的数值(下标从 0 开始),并将结果对109+ 7取余。如果下标大于等于序列的长度,请返回-1。
示例:
输入:
["Fancy", "append", "addAll", "append", "multAll", "getIndex", "addAll", "append", "multAll", "getIndex", "getIndex", "getIndex"]
[[], [2], [3], [7], [2], [0], [3], [10], [2], [0], [1], [2]]
输出:
[null, null, null, null, null, 10, null, null, null, 26, 34, 20]
解释:
Fancy fancy = new Fancy();
fancy.append(2); // 奇妙序列:[2]
fancy.addAll(3); // 奇妙序列:[2+3] -> [5]
fancy.append(7); // 奇妙序列:[5, 7]
fancy.multAll(2); // 奇妙序列:[5*2, 7*2] -> [10, 14]
fancy.getIndex(0); // 返回 10
fancy.addAll(3); // 奇妙序列:[10+3, 14+3] -> [13, 17]
fancy.append(10); // 奇妙序列:[13, 17, 10]
fancy.multAll(2); // 奇妙序列:[13*2, 17*2, 10*2] -> [26, 34, 20]
fancy.getIndex(0); // 返回 26
fancy.getIndex(1); // 返回 34
fancy.getIndex(2); // 返回 20
提示:
1 <= val, inc, m <= 1000 <= idx <= 105- 总共最多会有
105 次对append,addAll,multAll和getIndex的调用。
解题方法:懒加载
先看加法
假设这道题只有加法操作没有乘法操作,应该怎么做?每次addAll操作都把所有数加上个 i n c inc inc?太耗时了,时间复杂度直接干到了 O ( n 2 ) O(n^2) O(n2)。
所以我们很容易想到懒加载,addAll操作到时候不真的加,只有getIndex读取一个数的时候再一块加上。使用一个变量 a d d add add存储每个数应该加上多少就可以了。
但是append操作呢?新append的 m m m发生在之前的addAll之后, m m m不应该加上已经累积的 a d d add add。所以append的时候直接把 m m m减去 a d d add add再加入数组中就可以了,这样读取到时候剪去的 a d d add add还会再加回来。
加上乘法
乘法操作也类似,使用一个变量 m u l mul mul记录每个数应该乘以多少就好了。但是注意:
例如一个数 a a a应有的操作是: ( a + a d d ) × m u l (a+add)\times mul (a+add)×mul,那么其实相当于 a × m u l + a d d × m u l a\times mul + add\times mul a×mul+add×mul。(令 a d d n e w = a d d × m u l add_{new}=add\times mul addnew=add×mul则相当于 a × m u l + a d d n e w a\times mul + add_{new} a×mul+addnew)
所以乘法操作时不但需要累乘 m u l mul mul,还要将加数 a d d add add也乘上 m u l mul mul变成新的加数 a d d add add。
但是append操作呢?和只有加法时候同理,由于getIndex读数时需要 a × m u l + a d d a\times mul+add a×mul+add,所以append v a l val val的时候提前剪去 a d d add add再除以 m u l mul mul就好了,即append一个 v a l − a d d m u l \frac{val-add}{mul} mulval−add。
模运算
好在模运算具有分配律, ( a + b ) % M O D = ( a % M O D + b % M O D ) % M O D , (a+b)\% MOD = (a \% MOD + b\% MOD)\% MOD, (a+b)%MOD=(a%MOD+b%MOD)%MOD,所以可以在加减乘除运算过程中多次取模结果不变。
注意质数模运算下的除法中, a a a除以 b b b对质数 M O D MOD MOD取模等于 a a a乘以 b b b对 M O D MOD MOD的逆元,即 a b m o d M O D = a × b M O D − 2 m o d M O D \frac{a}{b}\mod MOD = a\times b^{MOD-2}\mod MOD bamodMOD=a×bMOD−2modMOD,而 b x b^{x} bx可以使用快速幂在 log x \log x logx的时间复杂度内求出。
- 时间复杂度:append操作 O ( log M O D ) O(\log MOD) O(logMOD),其他操作 O ( 1 ) O(1) O(1)
- 空间复杂度 O ( a ) O(a) O(a),其中 a a a是append的次数
AC代码
C++
cpp
/*
* @LastEditTime: 2026-03-15 11:19:41
*/
typedef long long ll;
const ll MOD = 1e9 + 7;
class Num {
private:
ll val;
ll power(ll a, ll n) const {
ll ans = 1;
for (; n; n >>= 1) {
if (n & 1) {
ans = ans * a % MOD;
}
a = a * a % MOD;
}
return ans;
}
public:
Num(): val(0) {} // 即得初始化val
Num(ll n) : val(n % MOD) {}
Num(int n) : val(n % MOD) {}
Num operator+(const ll& b) const { return Num((val + b) % MOD); }
Num operator-(const ll& b) const { return Num((val - b + MOD) % MOD); }
Num operator*(const ll& b) const { return Num(val * b % MOD); }
Num operator/(const ll& b) const { return Num(val * power(b, MOD - 2) % MOD); }
Num operator+(const Num& b) const { return Num((val + b.val) % MOD); }
Num operator-(const Num& b) const { return Num((val - b.val + MOD) % MOD); }
Num operator*(const Num& b) const { return Num(val * b.val % MOD); }
Num operator/(const Num& b) const { return Num(val * power(b.val, MOD - 2) % MOD); }
Num& operator+=(const ll& b) { return *this = *this + b; }
Num& operator-=(const ll& b) { return *this = *this - b; }
Num& operator*=(const ll& b) { return *this = *this * b; }
Num& operator/=(const ll& b) { return *this = *this / b; }
operator int() const { return static_cast<int>(val); }
};
class Fancy {
private:
vector<Num> vals;
Num add;
Num mul;
public:
Fancy() {
mul = Num(1);
}
void append(int val) {
vals.push_back((Num(val) - add) / mul);
}
void addAll(int inc) {
add += inc;
}
void multAll(int m) {
add *= m;
mul *= m;
}
int getIndex(int idx) {
return idx >= vals.size() ? -1 : (int)(vals[idx] * mul + add);
}
};
/**
* Your Fancy object will be instantiated and called as such:
* Fancy* obj = new Fancy();
* obj->append(val);
* obj->addAll(inc);
* obj->multAll(m);
* int param_4 = obj->getIndex(idx);
*/
#ifdef _DEBUG
/*
["Fancy","append","addAll","append","multAll","getIndex","addAll","append","multAll","getIndex","getIndex","getIndex"]
[[],[2],[3],[7],[2],[0],[3],[10],[2],[0],[1],[2]]
*/
int main() {
string a, b;
while (cin >> a >> b) {
vector<string> va = stringToVectorString(a);
vector<vector<int>> vb = stringToVectorVector(b);
if (va.size() != vb.size()) {
cout << "ERROR length" << endl;
continue;
}
Fancy fancy;
for (int i = 0; i < va.size(); i++) {
string s = va[i];
if (!s.empty() && s[0] == '"') {
s.erase(s.begin());
}
if (!s.empty() && s.back() == '"') {
s.erase(prev(s.end()));
}
int val = vb[i].empty() ? 0 : vb[i][0];
if (s == "Fancy") {
fancy = Fancy();
} else if (s == "append") {
fancy.append(val);
} else if (s == "addAll") {
fancy.addAll(val);
} else if (s == "multAll") {
fancy.multAll(val);
} else if (s == "getIndex") {
cout << fancy.getIndex(val) << endl;
} else {
cout << "ERROR" << endl;
}
}
}
return 0;
}
#endif
Python
python
'''
LastEditTime: 2026-03-15 11:25:58
'''
MOD = 1000000007
class Fancy:
def __init__(self):
self.add = 0
self.mul = 1
self.vals = []
def append(self, val: int) -> None:
self.vals.append((val - self.add + MOD) * pow(self.mul, -1, MOD) % MOD)
def addAll(self, inc: int) -> None:
self.add = (self.add + inc) % MOD
def multAll(self, m: int) -> None:
self.add = self.add * m % MOD
self.mul = self.mul * m % MOD
def getIndex(self, idx: int) -> int:
if idx >= len(self.vals):
return -1
return (self.vals[idx] * self.mul + self.add) % MOD
# Your Fancy object will be instantiated and called as such:
# obj = Fancy()
# obj.append(val)
# obj.addAll(inc)
# obj.multAll(m)
# param_4 = obj.getIndex(idx)
这道题也可以使用线段树来做。
同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~
千篇源码题解已开源