|
| 1 | +### 题目描述 |
| 2 | + |
| 3 | +这是 LeetCode 上的 **[1629. 按键持续时间最长的键](https://leetcode-cn.com/problems/slowest-key/solution/gong-shui-san-xie-jian-dan-mo-ni-ti-by-a-zjwb/)** ,难度为 **困难**。 |
| 4 | + |
| 5 | +Tag : 「线段树」 |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +请你实现三个 API `append`,`addAll` 和 `multAll` 来实现奇妙序列。 |
| 10 | + |
| 11 | +请实现 `Fancy` 类 : |
| 12 | + |
| 13 | +* `Fancy()` 初始化一个空序列对象。 |
| 14 | +* `void append(val)` 将整数 `val` 添加在序列末尾。 |
| 15 | +* `void addAll(inc)` 将所有序列中的现有数值都增加 `inc` 。 |
| 16 | +* `void multAll(m)` 将序列中的所有现有数值都乘以整数 `m` 。 |
| 17 | +* `int getIndex(idx)` 得到下标为 `idx` 处的数值(下标从 0ドル$ 开始),并将结果对 10ドル^9 + 7$ 取余。如果下标大于等于序列的长度,请返回 $-1$ 。 |
| 18 | + |
| 19 | +示例: |
| 20 | +``` |
| 21 | +输入: |
| 22 | +["Fancy", "append", "addAll", "append", "multAll", "getIndex", "addAll", "append", "multAll", "getIndex", "getIndex", "getIndex"] |
| 23 | +[[], [2], [3], [7], [2], [0], [3], [10], [2], [0], [1], [2]] |
| 24 | + |
| 25 | +输出: |
| 26 | +[null, null, null, null, null, 10, null, null, null, 26, 34, 20] |
| 27 | + |
| 28 | +解释: |
| 29 | +Fancy fancy = new Fancy(); |
| 30 | +fancy.append(2); // 奇妙序列:[2] |
| 31 | +fancy.addAll(3); // 奇妙序列:[2+3] -> [5] |
| 32 | +fancy.append(7); // 奇妙序列:[5, 7] |
| 33 | +fancy.multAll(2); // 奇妙序列:[5*2, 7*2] -> [10, 14] |
| 34 | +fancy.getIndex(0); // 返回 10 |
| 35 | +fancy.addAll(3); // 奇妙序列:[10+3, 14+3] -> [13, 17] |
| 36 | +fancy.append(10); // 奇妙序列:[13, 17, 10] |
| 37 | +fancy.multAll(2); // 奇妙序列:[13*2, 17*2, 10*2] -> [26, 34, 20] |
| 38 | +fancy.getIndex(0); // 返回 26 |
| 39 | +fancy.getIndex(1); // 返回 34 |
| 40 | +fancy.getIndex(2); // 返回 20 |
| 41 | +``` |
| 42 | + |
| 43 | +提示: |
| 44 | +* 1ドル <= val, inc, m <= 100$ |
| 45 | +* 0ドル <= idx <= 10^5$ |
| 46 | +* 总共最多会有 10ドル^5$ 次对 `append`,`addAll`,`multAll` 和 `getIndex` 的调用。 |
| 47 | + |
| 48 | +--- |
| 49 | + |
| 50 | +### 线段树(多个懒标记) |
| 51 | + |
| 52 | + |
| 53 | + |
| 54 | +代码: |
| 55 | +```Java |
| 56 | +class Fancy { |
| 57 | + class Node { |
| 58 | + int ls, rs; |
| 59 | + long val, add, mul = 1; |
| 60 | + } |
| 61 | + int N = (int) 1e8 + 10, M = 1000010, loc = 1, cnt = 0, mod = (int)1e9+7; |
| 62 | + Node[] tr = new Node[M]; |
| 63 | + void add(int u, int lc, int rc, int l, int r, int v) { |
| 64 | + int len = rc - lc + 1; |
| 65 | + if (l <= lc && rc <= r) { |
| 66 | + tr[u].val += len * v; tr[u].val %= mod; |
| 67 | + tr[u].add += v; tr[u].add %= mod; |
| 68 | + return ; |
| 69 | + } |
| 70 | + pushdown(u, len); |
| 71 | + int mid = lc + rc >> 1; |
| 72 | + if (l <= mid) add(tr[u].ls, lc, mid, l, r, v); |
| 73 | + if (r > mid) add(tr[u].rs, mid + 1, rc, l, r, v); |
| 74 | + pushup(u); |
| 75 | + } |
| 76 | + void mul(int u, int lc, int rc, int l, int r, int v) { |
| 77 | + if (l <= lc && rc <= r) { |
| 78 | + tr[u].val *= v; tr[u].val %= mod; |
| 79 | + tr[u].mul *= v; tr[u].mul %= mod; |
| 80 | + tr[u].add *= v; tr[u].add %= mod; |
| 81 | + return ; |
| 82 | + } |
| 83 | + pushdown(u, rc - lc + 1); |
| 84 | + int mid = lc + rc >> 1; |
| 85 | + if (l <= mid) mul(tr[u].ls, lc, mid, l, r, v); |
| 86 | + if (r > mid) mul(tr[u].rs, mid + 1, rc, l, r, v); |
| 87 | + pushup(u); |
| 88 | + } |
| 89 | + long query(int u, int lc, int rc, int l, int r) { |
| 90 | + if (l <= lc && rc <= r) return tr[u].val; |
| 91 | + pushdown(u, rc - lc + 1); |
| 92 | + int mid = lc + rc >> 1; |
| 93 | + long ans = 0; |
| 94 | + if (l <= mid) ans = query(tr[u].ls, lc, mid, l, r); |
| 95 | + if (r > mid) ans += query(tr[u].rs, mid + 1, rc, l, r); |
| 96 | + return ans; |
| 97 | + } |
| 98 | + void pushdown(int u, int len) { |
| 99 | + if (tr[u] == null) tr[u] = new Node(); |
| 100 | + if (tr[u].ls == 0) { |
| 101 | + tr[u].ls = ++loc; |
| 102 | + tr[tr[u].ls] = new Node(); |
| 103 | + } |
| 104 | + if (tr[u].rs == 0) { |
| 105 | + tr[u].rs = ++loc; |
| 106 | + tr[tr[u].rs] = new Node(); |
| 107 | + } |
| 108 | + long mul = tr[u].mul, add = tr[u].add; |
| 109 | + tr[tr[u].ls].val = tr[tr[u].ls].val * mul + (len / 2) * add; tr[tr[u].rs].val = tr[tr[u].rs].val * mul + (len / 2) * add; |
| 110 | + tr[tr[u].ls].mul *= mul; tr[tr[u].rs].mul *= mul; |
| 111 | + tr[tr[u].ls].add = tr[tr[u].ls].add * mul + add; tr[tr[u].rs].add = tr[tr[u].rs].add * mul + add; |
| 112 | + tr[tr[u].ls].val %= mod; tr[tr[u].rs].val %= mod; |
| 113 | + tr[tr[u].ls].mul %= mod; tr[tr[u].rs].mul %= mod; |
| 114 | + tr[tr[u].ls].add %= mod; tr[tr[u].rs].add %= mod; |
| 115 | + tr[u].add = 0; tr[u].mul = 1; |
| 116 | + } |
| 117 | + void pushup(int u) { |
| 118 | + tr[u].val = tr[tr[u].ls].val + tr[tr[u].rs].val; |
| 119 | + tr[u].val %= mod; |
| 120 | + } |
| 121 | + public void append(int val) { |
| 122 | + cnt++; |
| 123 | + add(1, 1, N, cnt, cnt, val); |
| 124 | + } |
| 125 | + public void addAll(int inc) { |
| 126 | + if (cnt == 0) return ; |
| 127 | + add(1, 1, N, 1, cnt, inc); |
| 128 | + } |
| 129 | + public void multAll(int m) { |
| 130 | + if (cnt == 0) return ; |
| 131 | + mul(1, 1, N, 1, cnt, m); |
| 132 | + } |
| 133 | + public int getIndex(int idx) { |
| 134 | + return idx + 1 > cnt ? -1 : (int)(query(1, 1, N, idx + 1, idx + 1) % mod); |
| 135 | + } |
| 136 | +} |
| 137 | +``` |
| 138 | +* 时间复杂度:查询次数为 $m,ドル值域大小为 $n,ドル插入和查询复杂度均为 $O(\log{n})$ |
| 139 | +* 空间复杂度:$O(m * \log{n})$ |
| 140 | + |
| 141 | +--- |
| 142 | + |
| 143 | +### 最后 |
| 144 | + |
| 145 | +这是我们「刷穿 LeetCode」系列文章的第 `No.1622` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 |
| 146 | + |
| 147 | +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 |
| 148 | + |
| 149 | +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 |
| 150 | + |
| 151 | +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 |
| 152 | + |
0 commit comments