LeetCode 1622.奇妙序列:懒更新

【LetMeFly】1622.奇妙序列:懒更新

力扣题目链接:https://leetcode.cn/problems/fancy-sequence/

请你实现三个 API appendaddAllmultAll 来实现奇妙序列。

请实现 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 <= 100
  • 0 <= idx <= 105
  • 总共最多会有 105 次对 appendaddAllmultAllgetIndex 的调用。

解题方法:懒加载

先看加法

假设这道题只有加法操作没有乘法操作,应该怎么做?每次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和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~

千篇源码题解已开源

相关推荐
無限進步D2 小时前
高精度算法 cpp
c++·笔记·算法·入门
qyzm2 小时前
AtCoder Beginner Contest 449
数据结构·python·算法·贪心算法
liuyao_xianhui2 小时前
优选算法_判断字符是否唯一_C++
java·开发语言·数据结构·c++·算法·链表
Barkamin2 小时前
希尔排序的简单实现
数据结构·算法·排序算法
Eward-an2 小时前
CKA/CKS认证通关复盘:实验环境搭建避坑指南,版本雷区+高效练习全解析
算法
Flying pigs~~2 小时前
自然语言处理(NLP)开胃小菜
人工智能·算法·自然语言处理·大模型·文本分析处理
Yupureki2 小时前
《C++实战项目-高并发内存池》7.大块内存的申请与释放
服务器·c语言·开发语言·c++·算法·哈希算法
2401_889884662 小时前
模板代码模块化设计
开发语言·c++·算法
aaa7872 小时前
Codeforces Round 1086 (Div. 2) 题解
算法