diff --git a/solution/2800-2899/2815.Max Pair Sum in an Array/README_EN.md b/solution/2800-2899/2815.Max Pair Sum in an Array/README_EN.md index df9eeae520f20..12ba90a68c0e0 100644 --- a/solution/2800-2899/2815.Max Pair Sum in an Array/README_EN.md +++ b/solution/2800-2899/2815.Max Pair Sum in an Array/README_EN.md @@ -37,6 +37,12 @@ It can be shown that there are no other pairs with equal maximum digits, so the ## Solutions +**Solution 1: Enumeration** + +First, we initialize the answer variable $ans=-1$. Next, we directly enumerate all pairs $(nums[i], nums[j])$ where $i \lt j$, and calculate their sum $v=nums[i] + nums[j]$. If $v$ is greater than $ans$ and the largest digit of $nums[i]$ and $nums[j]$ are the same, then we update $ans$ with $v$. + +The time complexity is $O(n^2 \times \log M)$, where $n$ is the length of the array and $M$ is the maximum value in the array. + ### **Python3** diff --git a/solution/2800-2899/2816.Double a Number Represented as a Linked List/README_EN.md b/solution/2800-2899/2816.Double a Number Represented as a Linked List/README_EN.md index 111a3e75e3e91..f53ca6810eaf7 100644 --- a/solution/2800-2899/2816.Double a Number Represented as a Linked List/README_EN.md +++ b/solution/2800-2899/2816.Double a Number Represented as a Linked List/README_EN.md @@ -36,6 +36,12 @@ ## Solutions +**Solution 1: Reverse Linked List + Simulation** + +First, we reverse the linked list, then simulate the multiplication operation, and finally reverse the linked list back. + +Time complexity is $O(n)$, where $n$ is the length of the linked list. Ignoring the space taken by the answer linked list, the space complexity is $O(1)$. + ### **Python3** diff --git a/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/README.md b/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/README.md index cd2b69b1823af..82beb52e88f52 100644 --- a/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/README.md +++ b/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/README.md @@ -84,11 +84,11 @@ class Solution: ans = inf for i in range(x, len(nums)): sl.add(nums[i - x]) - p = bisect_left(sl, nums[i]) - if p < len(sl): - ans = min(ans, abs(nums[i] - sl[p])) - if p: - ans = min(ans, abs(nums[i] - sl[p - 1])) + j = bisect_left(sl, nums[i]) + if j < len(sl): + ans = min(ans, sl[j] - nums[i]) + if j: + ans = min(ans, nums[i] - sl[j - 1]) return ans ``` @@ -144,7 +144,557 @@ public: ### **Go** ```go +func minAbsoluteDifference(nums []int, x int) int { + rbt := redblacktree.NewWithIntComparator() + ans := 1 << 30 + for i := x; i < len(nums); i++ { + rbt.Put(nums[i-x], nil) + c, _ := rbt.Ceiling(nums[i]) + f, _ := rbt.Floor(nums[i]) + if c != nil { + ans = min(ans, c.Key.(int)-nums[i]) + } + if f != nil { + ans = min(ans, nums[i]-f.Key.(int)) + } + } + return ans +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +### **TypeScript** + +```ts +function minAbsoluteDifference(nums: number[], x: number): number { + const s = new TreeMultiSet(); + const inf = 1 << 30; + let ans = inf; + for (let i = x; i < nums.length; ++i) { + s.add(nums[i - x]); + const c = s.ceil(nums[i]); + const f = s.floor(nums[i]); + if (c) { + ans = Math.min(ans, c - nums[i]); + } + if (f) { + ans = Math.min(ans, nums[i] - f); + } + } + return ans; +} + +type Compare = (lhs: T, rhs: T) => number; + +class RBTreeNode { + data: T; + count: number; + left: RBTreeNode | null; + right: RBTreeNode | null; + parent: RBTreeNode | null; + color: number; + constructor(data: T) { + this.data = data; + this.left = this.right = this.parent = null; + this.color = 0; + this.count = 1; + } + + sibling(): RBTreeNode | null { + if (!this.parent) return null; // sibling null if no parent + return this.isOnLeft() ? this.parent.right : this.parent.left; + } + + isOnLeft(): boolean { + return this === this.parent!.left; + } + + hasRedChild(): boolean { + return ( + Boolean(this.left && this.left.color === 0) || + Boolean(this.right && this.right.color === 0) + ); + } +} + +class RBTree { + root: RBTreeNode | null; + lt: (l: T, r: T) => boolean; + constructor( + compare: Compare = (l: T, r: T) => (l < r ? -1 : l > r ? 1 : 0), + ) { + this.root = null; + this.lt = (l: T, r: T) => compare(l, r) < 0; + } + + rotateLeft(pt: RBTreeNode): void { + const right = pt.right!; + pt.right = right.left; + + if (pt.right) pt.right.parent = pt; + right.parent = pt.parent; + + if (!pt.parent) this.root = right; + else if (pt === pt.parent.left) pt.parent.left = right; + else pt.parent.right = right; + + right.left = pt; + pt.parent = right; + } + + rotateRight(pt: RBTreeNode): void { + const left = pt.left!; + pt.left = left.right; + + if (pt.left) pt.left.parent = pt; + left.parent = pt.parent; + + if (!pt.parent) this.root = left; + else if (pt === pt.parent.left) pt.parent.left = left; + else pt.parent.right = left; + + left.right = pt; + pt.parent = left; + } + + swapColor(p1: RBTreeNode, p2: RBTreeNode): void { + const tmp = p1.color; + p1.color = p2.color; + p2.color = tmp; + } + + swapData(p1: RBTreeNode, p2: RBTreeNode): void { + const tmp = p1.data; + p1.data = p2.data; + p2.data = tmp; + } + + fixAfterInsert(pt: RBTreeNode): void { + let parent = null; + let grandParent = null; + + while (pt !== this.root && pt.color !== 1 && pt.parent?.color === 0) { + parent = pt.parent; + grandParent = pt.parent.parent; + + /* Case : A + Parent of pt is left child of Grand-parent of pt */ + if (parent === grandParent?.left) { + const uncle = grandParent.right; + + /* Case : 1 + The uncle of pt is also red + Only Recoloring required */ + if (uncle && uncle.color === 0) { + grandParent.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent; + } else { + /* Case : 2 + pt is right child of its parent + Left-rotation required */ + if (pt === parent.right) { + this.rotateLeft(parent); + pt = parent; + parent = pt.parent; + } + + /* Case : 3 + pt is left child of its parent + Right-rotation required */ + this.rotateRight(grandParent); + this.swapColor(parent!, grandParent); + pt = parent!; + } + } else { + /* Case : B + Parent of pt is right child of Grand-parent of pt */ + const uncle = grandParent!.left; + + /* Case : 1 + The uncle of pt is also red + Only Recoloring required */ + if (uncle != null && uncle.color === 0) { + grandParent!.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent!; + } else { + /* Case : 2 + pt is left child of its parent + Right-rotation required */ + if (pt === parent.left) { + this.rotateRight(parent); + pt = parent; + parent = pt.parent; + } + + /* Case : 3 + pt is right child of its parent + Left-rotation required */ + this.rotateLeft(grandParent!); + this.swapColor(parent!, grandParent!); + pt = parent!; + } + } + } + this.root!.color = 1; + } + + delete(val: T): boolean { + const node = this.find(val); + if (!node) return false; + node.count--; + if (!node.count) this.deleteNode(node); + return true; + } + + deleteAll(val: T): boolean { + const node = this.find(val); + if (!node) return false; + this.deleteNode(node); + return true; + } + + deleteNode(v: RBTreeNode): void { + const u = BSTreplace(v); + + // True when u and v are both black + const uvBlack = (u === null || u.color === 1) && v.color === 1; + const parent = v.parent!; + + if (!u) { + // u is null therefore v is leaf + if (v === this.root) this.root = null; + // v is root, making root null + else { + if (uvBlack) { + // u and v both black + // v is leaf, fix double black at v + this.fixDoubleBlack(v); + } else { + // u or v is red + if (v.sibling()) { + // sibling is not null, make it red" + v.sibling()!.color = 0; + } + } + // delete v from the tree + if (v.isOnLeft()) parent.left = null; + else parent.right = null; + } + return; + } + + if (!v.left || !v.right) { + // v has 1 child + if (v === this.root) { + // v is root, assign the value of u to v, and delete u + v.data = u.data; + v.left = v.right = null; + } else { + // Detach v from tree and move u up + if (v.isOnLeft()) parent.left = u; + else parent.right = u; + u.parent = parent; + if (uvBlack) this.fixDoubleBlack(u); + // u and v both black, fix double black at u + else u.color = 1; // u or v red, color u black + } + return; + } + + // v has 2 children, swap data with successor and recurse + this.swapData(u, v); + this.deleteNode(u); + + // find node that replaces a deleted node in BST + function BSTreplace(x: RBTreeNode): RBTreeNode | null { + // when node have 2 children + if (x.left && x.right) return successor(x.right); + // when leaf + if (!x.left && !x.right) return null; + // when single child + return x.left ?? x.right; + } + // find node that do not have a left child + // in the subtree of the given node + function successor(x: RBTreeNode): RBTreeNode { + let temp = x; + while (temp.left) temp = temp.left; + return temp; + } + } + + fixDoubleBlack(x: RBTreeNode): void { + if (x === this.root) return; // Reached root + + const sibling = x.sibling(); + const parent = x.parent!; + if (!sibling) { + // No sibiling, double black pushed up + this.fixDoubleBlack(parent); + } else { + if (sibling.color === 0) { + // Sibling red + parent.color = 0; + sibling.color = 1; + if (sibling.isOnLeft()) this.rotateRight(parent); + // left case + else this.rotateLeft(parent); // right case + this.fixDoubleBlack(x); + } else { + // Sibling black + if (sibling.hasRedChild()) { + // at least 1 red children + if (sibling.left && sibling.left.color === 0) { + if (sibling.isOnLeft()) { + // left left + sibling.left.color = sibling.color; + sibling.color = parent.color; + this.rotateRight(parent); + } else { + // right left + sibling.left.color = parent.color; + this.rotateRight(sibling); + this.rotateLeft(parent); + } + } else { + if (sibling.isOnLeft()) { + // left right + sibling.right!.color = parent.color; + this.rotateLeft(sibling); + this.rotateRight(parent); + } else { + // right right + sibling.right!.color = sibling.color; + sibling.color = parent.color; + this.rotateLeft(parent); + } + } + parent.color = 1; + } else { + // 2 black children + sibling.color = 0; + if (parent.color === 1) this.fixDoubleBlack(parent); + else parent.color = 1; + } + } + } + } + + insert(data: T): boolean { + // search for a position to insert + let parent = this.root; + while (parent) { + if (this.lt(data, parent.data)) { + if (!parent.left) break; + else parent = parent.left; + } else if (this.lt(parent.data, data)) { + if (!parent.right) break; + else parent = parent.right; + } else break; + } + // insert node into parent + const node = new RBTreeNode(data); + if (!parent) this.root = node; + else if (this.lt(node.data, parent.data)) parent.left = node; + else if (this.lt(parent.data, node.data)) parent.right = node; + else { + parent.count++; + return false; + } + node.parent = parent; + this.fixAfterInsert(node); + return true; + } + + find(data: T): RBTreeNode | null { + let p = this.root; + while (p) { + if (this.lt(data, p.data)) { + p = p.left; + } else if (this.lt(p.data, data)) { + p = p.right; + } else break; + } + return p ?? null; + } + + *inOrder(root: RBTreeNode = this.root!): Generator { + if (!root) return; + for (const v of this.inOrder(root.left!)) yield v; + yield root.data; + for (const v of this.inOrder(root.right!)) yield v; + } + + *reverseInOrder( + root: RBTreeNode = this.root!, + ): Generator { + if (!root) return; + for (const v of this.reverseInOrder(root.right!)) yield v; + yield root.data; + for (const v of this.reverseInOrder(root.left!)) yield v; + } +} + +class TreeMultiSet { + _size: number; + tree: RBTree; + compare: Compare; + constructor( + collection: T[] | Compare = [], + compare: Compare = (l: T, r: T) => (l < r ? -1 : l > r ? 1 : 0), + ) { + if (typeof collection === 'function') { + compare = collection; + collection = []; + } + this._size = 0; + this.compare = compare; + this.tree = new RBTree(compare); + for (const val of collection) this.add(val); + } + + size(): number { + return this._size; + } + + has(val: T): boolean { + return !!this.tree.find(val); + } + + add(val: T): boolean { + const successful = this.tree.insert(val); + this._size++; + return successful; + } + + delete(val: T): boolean { + const successful = this.tree.delete(val); + if (!successful) return false; + this._size--; + return true; + } + + count(val: T): number { + const node = this.tree.find(val); + return node ? node.count : 0; + } + + ceil(val: T): T | undefined { + let p = this.tree.root; + let higher = null; + while (p) { + if (this.compare(p.data, val) >= 0) { + higher = p; + p = p.left; + } else { + p = p.right; + } + } + return higher?.data; + } + + floor(val: T): T | undefined { + let p = this.tree.root; + let lower = null; + while (p) { + if (this.compare(val, p.data) >= 0) { + lower = p; + p = p.right; + } else { + p = p.left; + } + } + return lower?.data; + } + + higher(val: T): T | undefined { + let p = this.tree.root; + let higher = null; + while (p) { + if (this.compare(val, p.data) < 0) { + higher = p; + p = p.left; + } else { + p = p.right; + } + } + return higher?.data; + } + + lower(val: T): T | undefined { + let p = this.tree.root; + let lower = null; + while (p) { + if (this.compare(p.data, val) < 0) { + lower = p; + p = p.right; + } else { + p = p.left; + } + } + return lower?.data; + } + + first(): T | undefined { + return this.tree.inOrder().next().value; + } + + last(): T | undefined { + return this.tree.reverseInOrder().next().value; + } + + shift(): T | undefined { + const first = this.first(); + if (first === undefined) return undefined; + this.delete(first); + return first; + } + + pop(): T | undefined { + const last = this.last(); + if (last === undefined) return undefined; + this.delete(last); + return last; + } + + *[Symbol.iterator](): Generator { + yield* this.values(); + } + + *keys(): Generator { + for (const val of this.values()) yield val; + } + + *values(): Generator { + for (const val of this.tree.inOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } + + /** + * Return a generator for reverse order traversing the multi-set + */ + *rvalues(): Generator { + for (const val of this.tree.reverseInOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } +} ``` ### **...** diff --git a/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/README_EN.md b/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/README_EN.md index 57247535c1f2f..a67428e22b7df 100644 --- a/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/README_EN.md +++ b/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/README_EN.md @@ -54,6 +54,14 @@ It can be shown that 3 is the optimal answer. ## Solutions +**Solution 1: Ordered Set** + +We create an ordered set to store the elements whose distance to the current index is at least $x$. + +Next, we enumerate from index $i = x$, each time we add $nums[i - x]$ into the ordered set. Then we find the two elements in the ordered set which are closest to $nums[i]$, and the minimum absolute difference between them is the answer. + +The time complexity is $O(n \times \log n)$, and the space complexity is $O(n)$. Where $n$ is the length of array $nums$. + ### **Python3** @@ -68,11 +76,11 @@ class Solution: ans = inf for i in range(x, len(nums)): sl.add(nums[i - x]) - p = bisect_left(sl, nums[i]) - if p < len(sl): - ans = min(ans, abs(nums[i] - sl[p])) - if p: - ans = min(ans, abs(nums[i] - sl[p - 1])) + j = bisect_left(sl, nums[i]) + if j < len(sl): + ans = min(ans, sl[j] - nums[i]) + if j: + ans = min(ans, nums[i] - sl[j - 1]) return ans ``` @@ -126,7 +134,557 @@ public: ### **Go** ```go +func minAbsoluteDifference(nums []int, x int) int { + rbt := redblacktree.NewWithIntComparator() + ans := 1 << 30 + for i := x; i < len(nums); i++ { + rbt.Put(nums[i-x], nil) + c, _ := rbt.Ceiling(nums[i]) + f, _ := rbt.Floor(nums[i]) + if c != nil { + ans = min(ans, c.Key.(int)-nums[i]) + } + if f != nil { + ans = min(ans, nums[i]-f.Key.(int)) + } + } + return ans +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +### **TypeScript** + +```ts +function minAbsoluteDifference(nums: number[], x: number): number { + const s = new TreeMultiSet(); + const inf = 1 << 30; + let ans = inf; + for (let i = x; i < nums.length; ++i) { + s.add(nums[i - x]); + const c = s.ceil(nums[i]); + const f = s.floor(nums[i]); + if (c) { + ans = Math.min(ans, c - nums[i]); + } + if (f) { + ans = Math.min(ans, nums[i] - f); + } + } + return ans; +} + +type Compare = (lhs: T, rhs: T) => number; + +class RBTreeNode { + data: T; + count: number; + left: RBTreeNode | null; + right: RBTreeNode | null; + parent: RBTreeNode | null; + color: number; + constructor(data: T) { + this.data = data; + this.left = this.right = this.parent = null; + this.color = 0; + this.count = 1; + } + + sibling(): RBTreeNode | null { + if (!this.parent) return null; // sibling null if no parent + return this.isOnLeft() ? this.parent.right : this.parent.left; + } + + isOnLeft(): boolean { + return this === this.parent!.left; + } + + hasRedChild(): boolean { + return ( + Boolean(this.left && this.left.color === 0) || + Boolean(this.right && this.right.color === 0) + ); + } +} + +class RBTree { + root: RBTreeNode | null; + lt: (l: T, r: T) => boolean; + constructor( + compare: Compare = (l: T, r: T) => (l < r ? -1 : l > r ? 1 : 0), + ) { + this.root = null; + this.lt = (l: T, r: T) => compare(l, r) < 0; + } + + rotateLeft(pt: RBTreeNode): void { + const right = pt.right!; + pt.right = right.left; + + if (pt.right) pt.right.parent = pt; + right.parent = pt.parent; + + if (!pt.parent) this.root = right; + else if (pt === pt.parent.left) pt.parent.left = right; + else pt.parent.right = right; + + right.left = pt; + pt.parent = right; + } + + rotateRight(pt: RBTreeNode): void { + const left = pt.left!; + pt.left = left.right; + + if (pt.left) pt.left.parent = pt; + left.parent = pt.parent; + + if (!pt.parent) this.root = left; + else if (pt === pt.parent.left) pt.parent.left = left; + else pt.parent.right = left; + + left.right = pt; + pt.parent = left; + } + + swapColor(p1: RBTreeNode, p2: RBTreeNode): void { + const tmp = p1.color; + p1.color = p2.color; + p2.color = tmp; + } + + swapData(p1: RBTreeNode, p2: RBTreeNode): void { + const tmp = p1.data; + p1.data = p2.data; + p2.data = tmp; + } + + fixAfterInsert(pt: RBTreeNode): void { + let parent = null; + let grandParent = null; + + while (pt !== this.root && pt.color !== 1 && pt.parent?.color === 0) { + parent = pt.parent; + grandParent = pt.parent.parent; + + /* Case : A + Parent of pt is left child of Grand-parent of pt */ + if (parent === grandParent?.left) { + const uncle = grandParent.right; + + /* Case : 1 + The uncle of pt is also red + Only Recoloring required */ + if (uncle && uncle.color === 0) { + grandParent.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent; + } else { + /* Case : 2 + pt is right child of its parent + Left-rotation required */ + if (pt === parent.right) { + this.rotateLeft(parent); + pt = parent; + parent = pt.parent; + } + + /* Case : 3 + pt is left child of its parent + Right-rotation required */ + this.rotateRight(grandParent); + this.swapColor(parent!, grandParent); + pt = parent!; + } + } else { + /* Case : B + Parent of pt is right child of Grand-parent of pt */ + const uncle = grandParent!.left; + + /* Case : 1 + The uncle of pt is also red + Only Recoloring required */ + if (uncle != null && uncle.color === 0) { + grandParent!.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent!; + } else { + /* Case : 2 + pt is left child of its parent + Right-rotation required */ + if (pt === parent.left) { + this.rotateRight(parent); + pt = parent; + parent = pt.parent; + } + + /* Case : 3 + pt is right child of its parent + Left-rotation required */ + this.rotateLeft(grandParent!); + this.swapColor(parent!, grandParent!); + pt = parent!; + } + } + } + this.root!.color = 1; + } + + delete(val: T): boolean { + const node = this.find(val); + if (!node) return false; + node.count--; + if (!node.count) this.deleteNode(node); + return true; + } + + deleteAll(val: T): boolean { + const node = this.find(val); + if (!node) return false; + this.deleteNode(node); + return true; + } + + deleteNode(v: RBTreeNode): void { + const u = BSTreplace(v); + + // True when u and v are both black + const uvBlack = (u === null || u.color === 1) && v.color === 1; + const parent = v.parent!; + + if (!u) { + // u is null therefore v is leaf + if (v === this.root) this.root = null; + // v is root, making root null + else { + if (uvBlack) { + // u and v both black + // v is leaf, fix double black at v + this.fixDoubleBlack(v); + } else { + // u or v is red + if (v.sibling()) { + // sibling is not null, make it red" + v.sibling()!.color = 0; + } + } + // delete v from the tree + if (v.isOnLeft()) parent.left = null; + else parent.right = null; + } + return; + } + + if (!v.left || !v.right) { + // v has 1 child + if (v === this.root) { + // v is root, assign the value of u to v, and delete u + v.data = u.data; + v.left = v.right = null; + } else { + // Detach v from tree and move u up + if (v.isOnLeft()) parent.left = u; + else parent.right = u; + u.parent = parent; + if (uvBlack) this.fixDoubleBlack(u); + // u and v both black, fix double black at u + else u.color = 1; // u or v red, color u black + } + return; + } + + // v has 2 children, swap data with successor and recurse + this.swapData(u, v); + this.deleteNode(u); + + // find node that replaces a deleted node in BST + function BSTreplace(x: RBTreeNode): RBTreeNode | null { + // when node have 2 children + if (x.left && x.right) return successor(x.right); + // when leaf + if (!x.left && !x.right) return null; + // when single child + return x.left ?? x.right; + } + // find node that do not have a left child + // in the subtree of the given node + function successor(x: RBTreeNode): RBTreeNode { + let temp = x; + while (temp.left) temp = temp.left; + return temp; + } + } + + fixDoubleBlack(x: RBTreeNode): void { + if (x === this.root) return; // Reached root + + const sibling = x.sibling(); + const parent = x.parent!; + if (!sibling) { + // No sibiling, double black pushed up + this.fixDoubleBlack(parent); + } else { + if (sibling.color === 0) { + // Sibling red + parent.color = 0; + sibling.color = 1; + if (sibling.isOnLeft()) this.rotateRight(parent); + // left case + else this.rotateLeft(parent); // right case + this.fixDoubleBlack(x); + } else { + // Sibling black + if (sibling.hasRedChild()) { + // at least 1 red children + if (sibling.left && sibling.left.color === 0) { + if (sibling.isOnLeft()) { + // left left + sibling.left.color = sibling.color; + sibling.color = parent.color; + this.rotateRight(parent); + } else { + // right left + sibling.left.color = parent.color; + this.rotateRight(sibling); + this.rotateLeft(parent); + } + } else { + if (sibling.isOnLeft()) { + // left right + sibling.right!.color = parent.color; + this.rotateLeft(sibling); + this.rotateRight(parent); + } else { + // right right + sibling.right!.color = sibling.color; + sibling.color = parent.color; + this.rotateLeft(parent); + } + } + parent.color = 1; + } else { + // 2 black children + sibling.color = 0; + if (parent.color === 1) this.fixDoubleBlack(parent); + else parent.color = 1; + } + } + } + } + + insert(data: T): boolean { + // search for a position to insert + let parent = this.root; + while (parent) { + if (this.lt(data, parent.data)) { + if (!parent.left) break; + else parent = parent.left; + } else if (this.lt(parent.data, data)) { + if (!parent.right) break; + else parent = parent.right; + } else break; + } + + // insert node into parent + const node = new RBTreeNode(data); + if (!parent) this.root = node; + else if (this.lt(node.data, parent.data)) parent.left = node; + else if (this.lt(parent.data, node.data)) parent.right = node; + else { + parent.count++; + return false; + } + node.parent = parent; + this.fixAfterInsert(node); + return true; + } + + find(data: T): RBTreeNode | null { + let p = this.root; + while (p) { + if (this.lt(data, p.data)) { + p = p.left; + } else if (this.lt(p.data, data)) { + p = p.right; + } else break; + } + return p ?? null; + } + + *inOrder(root: RBTreeNode = this.root!): Generator { + if (!root) return; + for (const v of this.inOrder(root.left!)) yield v; + yield root.data; + for (const v of this.inOrder(root.right!)) yield v; + } + + *reverseInOrder( + root: RBTreeNode = this.root!, + ): Generator { + if (!root) return; + for (const v of this.reverseInOrder(root.right!)) yield v; + yield root.data; + for (const v of this.reverseInOrder(root.left!)) yield v; + } +} + +class TreeMultiSet { + _size: number; + tree: RBTree; + compare: Compare; + constructor( + collection: T[] | Compare = [], + compare: Compare = (l: T, r: T) => (l < r ? -1 : l > r ? 1 : 0), + ) { + if (typeof collection === 'function') { + compare = collection; + collection = []; + } + this._size = 0; + this.compare = compare; + this.tree = new RBTree(compare); + for (const val of collection) this.add(val); + } + + size(): number { + return this._size; + } + + has(val: T): boolean { + return !!this.tree.find(val); + } + + add(val: T): boolean { + const successful = this.tree.insert(val); + this._size++; + return successful; + } + + delete(val: T): boolean { + const successful = this.tree.delete(val); + if (!successful) return false; + this._size--; + return true; + } + + count(val: T): number { + const node = this.tree.find(val); + return node ? node.count : 0; + } + + ceil(val: T): T | undefined { + let p = this.tree.root; + let higher = null; + while (p) { + if (this.compare(p.data, val) >= 0) { + higher = p; + p = p.left; + } else { + p = p.right; + } + } + return higher?.data; + } + + floor(val: T): T | undefined { + let p = this.tree.root; + let lower = null; + while (p) { + if (this.compare(val, p.data) >= 0) { + lower = p; + p = p.right; + } else { + p = p.left; + } + } + return lower?.data; + } + + higher(val: T): T | undefined { + let p = this.tree.root; + let higher = null; + while (p) { + if (this.compare(val, p.data) < 0) { + higher = p; + p = p.left; + } else { + p = p.right; + } + } + return higher?.data; + } + + lower(val: T): T | undefined { + let p = this.tree.root; + let lower = null; + while (p) { + if (this.compare(p.data, val) < 0) { + lower = p; + p = p.right; + } else { + p = p.left; + } + } + return lower?.data; + } + + first(): T | undefined { + return this.tree.inOrder().next().value; + } + last(): T | undefined { + return this.tree.reverseInOrder().next().value; + } + + shift(): T | undefined { + const first = this.first(); + if (first === undefined) return undefined; + this.delete(first); + return first; + } + + pop(): T | undefined { + const last = this.last(); + if (last === undefined) return undefined; + this.delete(last); + return last; + } + + *[Symbol.iterator](): Generator { + yield* this.values(); + } + + *keys(): Generator { + for (const val of this.values()) yield val; + } + + *values(): Generator { + for (const val of this.tree.inOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } + + /** + * Return a generator for reverse order traversing the multi-set + */ + *rvalues(): Generator { + for (const val of this.tree.reverseInOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } +} ``` ### **...** diff --git a/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/Solution.go b/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/Solution.go new file mode 100644 index 0000000000000..3aa05c97b5d4a --- /dev/null +++ b/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/Solution.go @@ -0,0 +1,23 @@ +func minAbsoluteDifference(nums []int, x int) int { + rbt := redblacktree.NewWithIntComparator() + ans := 1 << 30 + for i := x; i < len(nums); i++ { + rbt.Put(nums[i-x], nil) + c, _ := rbt.Ceiling(nums[i]) + f, _ := rbt.Floor(nums[i]) + if c != nil { + ans = min(ans, c.Key.(int)-nums[i]) + } + if f != nil { + ans = min(ans, nums[i]-f.Key.(int)) + } + } + return ans +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} \ No newline at end of file diff --git a/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/Solution.py b/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/Solution.py index 37eb0dd140485..2c1697429e0e5 100644 --- a/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/Solution.py +++ b/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/Solution.py @@ -7,9 +7,9 @@ def minAbsoluteDifference(self, nums: List[int], x: int) -> int: ans = inf for i in range(x, len(nums)): sl.add(nums[i - x]) - p = bisect_left(sl, nums[i]) - if p < len(sl): - ans = min(ans, abs(nums[i] - sl[p])) - if p: - ans = min(ans, abs(nums[i] - sl[p - 1])) + j = bisect_left(sl, nums[i]) + if j < len(sl): + ans = min(ans, sl[j] - nums[i]) + if j: + ans = min(ans, nums[i] - sl[j - 1]) return ans diff --git a/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/Solution.ts b/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/Solution.ts new file mode 100644 index 0000000000000..8bd1adc8b1729 --- /dev/null +++ b/solution/2800-2899/2817.Minimum Absolute Difference Between Elements With Constraint/Solution.ts @@ -0,0 +1,523 @@ +function minAbsoluteDifference(nums: number[], x: number): number { + const s = new TreeMultiSet(); + const inf = 1 << 30; + let ans = inf; + for (let i = x; i < nums.length; ++i) { + s.add(nums[i - x]); + const c = s.ceil(nums[i]); + const f = s.floor(nums[i]); + if (c) { + ans = Math.min(ans, c - nums[i]); + } + if (f) { + ans = Math.min(ans, nums[i] - f); + } + } + return ans; +} + +type Compare = (lhs: T, rhs: T) => number; + +class RBTreeNode { + data: T; + count: number; + left: RBTreeNode | null; + right: RBTreeNode | null; + parent: RBTreeNode | null; + color: number; + constructor(data: T) { + this.data = data; + this.left = this.right = this.parent = null; + this.color = 0; + this.count = 1; + } + + sibling(): RBTreeNode | null { + if (!this.parent) return null; // sibling null if no parent + return this.isOnLeft() ? this.parent.right : this.parent.left; + } + + isOnLeft(): boolean { + return this === this.parent!.left; + } + + hasRedChild(): boolean { + return ( + Boolean(this.left && this.left.color === 0) || + Boolean(this.right && this.right.color === 0) + ); + } +} + +class RBTree { + root: RBTreeNode | null; + lt: (l: T, r: T) => boolean; + constructor( + compare: Compare = (l: T, r: T) => (l < r ? -1 : l > r ? 1 : 0), + ) { + this.root = null; + this.lt = (l: T, r: T) => compare(l, r) < 0; + } + + rotateLeft(pt: RBTreeNode): void { + const right = pt.right!; + pt.right = right.left; + + if (pt.right) pt.right.parent = pt; + right.parent = pt.parent; + + if (!pt.parent) this.root = right; + else if (pt === pt.parent.left) pt.parent.left = right; + else pt.parent.right = right; + + right.left = pt; + pt.parent = right; + } + + rotateRight(pt: RBTreeNode): void { + const left = pt.left!; + pt.left = left.right; + + if (pt.left) pt.left.parent = pt; + left.parent = pt.parent; + + if (!pt.parent) this.root = left; + else if (pt === pt.parent.left) pt.parent.left = left; + else pt.parent.right = left; + + left.right = pt; + pt.parent = left; + } + + swapColor(p1: RBTreeNode, p2: RBTreeNode): void { + const tmp = p1.color; + p1.color = p2.color; + p2.color = tmp; + } + + swapData(p1: RBTreeNode, p2: RBTreeNode): void { + const tmp = p1.data; + p1.data = p2.data; + p2.data = tmp; + } + + fixAfterInsert(pt: RBTreeNode): void { + let parent = null; + let grandParent = null; + + while (pt !== this.root && pt.color !== 1 && pt.parent?.color === 0) { + parent = pt.parent; + grandParent = pt.parent.parent; + + /* Case : A + Parent of pt is left child of Grand-parent of pt */ + if (parent === grandParent?.left) { + const uncle = grandParent.right; + + /* Case : 1 + The uncle of pt is also red + Only Recoloring required */ + if (uncle && uncle.color === 0) { + grandParent.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent; + } else { + /* Case : 2 + pt is right child of its parent + Left-rotation required */ + if (pt === parent.right) { + this.rotateLeft(parent); + pt = parent; + parent = pt.parent; + } + + /* Case : 3 + pt is left child of its parent + Right-rotation required */ + this.rotateRight(grandParent); + this.swapColor(parent!, grandParent); + pt = parent!; + } + } else { + /* Case : B + Parent of pt is right child of Grand-parent of pt */ + const uncle = grandParent!.left; + + /* Case : 1 + The uncle of pt is also red + Only Recoloring required */ + if (uncle != null && uncle.color === 0) { + grandParent!.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent!; + } else { + /* Case : 2 + pt is left child of its parent + Right-rotation required */ + if (pt === parent.left) { + this.rotateRight(parent); + pt = parent; + parent = pt.parent; + } + + /* Case : 3 + pt is right child of its parent + Left-rotation required */ + this.rotateLeft(grandParent!); + this.swapColor(parent!, grandParent!); + pt = parent!; + } + } + } + this.root!.color = 1; + } + + delete(val: T): boolean { + const node = this.find(val); + if (!node) return false; + node.count--; + if (!node.count) this.deleteNode(node); + return true; + } + + deleteAll(val: T): boolean { + const node = this.find(val); + if (!node) return false; + this.deleteNode(node); + return true; + } + + deleteNode(v: RBTreeNode): void { + const u = BSTreplace(v); + + // True when u and v are both black + const uvBlack = (u === null || u.color === 1) && v.color === 1; + const parent = v.parent!; + + if (!u) { + // u is null therefore v is leaf + if (v === this.root) this.root = null; + // v is root, making root null + else { + if (uvBlack) { + // u and v both black + // v is leaf, fix double black at v + this.fixDoubleBlack(v); + } else { + // u or v is red + if (v.sibling()) { + // sibling is not null, make it red" + v.sibling()!.color = 0; + } + } + // delete v from the tree + if (v.isOnLeft()) parent.left = null; + else parent.right = null; + } + return; + } + + if (!v.left || !v.right) { + // v has 1 child + if (v === this.root) { + // v is root, assign the value of u to v, and delete u + v.data = u.data; + v.left = v.right = null; + } else { + // Detach v from tree and move u up + if (v.isOnLeft()) parent.left = u; + else parent.right = u; + u.parent = parent; + if (uvBlack) this.fixDoubleBlack(u); + // u and v both black, fix double black at u + else u.color = 1; // u or v red, color u black + } + return; + } + + // v has 2 children, swap data with successor and recurse + this.swapData(u, v); + this.deleteNode(u); + + // find node that replaces a deleted node in BST + function BSTreplace(x: RBTreeNode): RBTreeNode | null { + // when node have 2 children + if (x.left && x.right) return successor(x.right); + // when leaf + if (!x.left && !x.right) return null; + // when single child + return x.left ?? x.right; + } + // find node that do not have a left child + // in the subtree of the given node + function successor(x: RBTreeNode): RBTreeNode { + let temp = x; + while (temp.left) temp = temp.left; + return temp; + } + } + + fixDoubleBlack(x: RBTreeNode): void { + if (x === this.root) return; // Reached root + + const sibling = x.sibling(); + const parent = x.parent!; + if (!sibling) { + // No sibiling, double black pushed up + this.fixDoubleBlack(parent); + } else { + if (sibling.color === 0) { + // Sibling red + parent.color = 0; + sibling.color = 1; + if (sibling.isOnLeft()) this.rotateRight(parent); + // left case + else this.rotateLeft(parent); // right case + this.fixDoubleBlack(x); + } else { + // Sibling black + if (sibling.hasRedChild()) { + // at least 1 red children + if (sibling.left && sibling.left.color === 0) { + if (sibling.isOnLeft()) { + // left left + sibling.left.color = sibling.color; + sibling.color = parent.color; + this.rotateRight(parent); + } else { + // right left + sibling.left.color = parent.color; + this.rotateRight(sibling); + this.rotateLeft(parent); + } + } else { + if (sibling.isOnLeft()) { + // left right + sibling.right!.color = parent.color; + this.rotateLeft(sibling); + this.rotateRight(parent); + } else { + // right right + sibling.right!.color = sibling.color; + sibling.color = parent.color; + this.rotateLeft(parent); + } + } + parent.color = 1; + } else { + // 2 black children + sibling.color = 0; + if (parent.color === 1) this.fixDoubleBlack(parent); + else parent.color = 1; + } + } + } + } + + insert(data: T): boolean { + // search for a position to insert + let parent = this.root; + while (parent) { + if (this.lt(data, parent.data)) { + if (!parent.left) break; + else parent = parent.left; + } else if (this.lt(parent.data, data)) { + if (!parent.right) break; + else parent = parent.right; + } else break; + } + + // insert node into parent + const node = new RBTreeNode(data); + if (!parent) this.root = node; + else if (this.lt(node.data, parent.data)) parent.left = node; + else if (this.lt(parent.data, node.data)) parent.right = node; + else { + parent.count++; + return false; + } + node.parent = parent; + this.fixAfterInsert(node); + return true; + } + + find(data: T): RBTreeNode | null { + let p = this.root; + while (p) { + if (this.lt(data, p.data)) { + p = p.left; + } else if (this.lt(p.data, data)) { + p = p.right; + } else break; + } + return p ?? null; + } + + *inOrder(root: RBTreeNode = this.root!): Generator { + if (!root) return; + for (const v of this.inOrder(root.left!)) yield v; + yield root.data; + for (const v of this.inOrder(root.right!)) yield v; + } + + *reverseInOrder( + root: RBTreeNode = this.root!, + ): Generator { + if (!root) return; + for (const v of this.reverseInOrder(root.right!)) yield v; + yield root.data; + for (const v of this.reverseInOrder(root.left!)) yield v; + } +} + +class TreeMultiSet { + _size: number; + tree: RBTree; + compare: Compare; + constructor( + collection: T[] | Compare = [], + compare: Compare = (l: T, r: T) => (l < r ? -1 : l > r ? 1 : 0), + ) { + if (typeof collection === 'function') { + compare = collection; + collection = []; + } + this._size = 0; + this.compare = compare; + this.tree = new RBTree(compare); + for (const val of collection) this.add(val); + } + + size(): number { + return this._size; + } + + has(val: T): boolean { + return !!this.tree.find(val); + } + + add(val: T): boolean { + const successful = this.tree.insert(val); + this._size++; + return successful; + } + + delete(val: T): boolean { + const successful = this.tree.delete(val); + if (!successful) return false; + this._size--; + return true; + } + + count(val: T): number { + const node = this.tree.find(val); + return node ? node.count : 0; + } + + ceil(val: T): T | undefined { + let p = this.tree.root; + let higher = null; + while (p) { + if (this.compare(p.data, val) >= 0) { + higher = p; + p = p.left; + } else { + p = p.right; + } + } + return higher?.data; + } + + floor(val: T): T | undefined { + let p = this.tree.root; + let lower = null; + while (p) { + if (this.compare(val, p.data) >= 0) { + lower = p; + p = p.right; + } else { + p = p.left; + } + } + return lower?.data; + } + + higher(val: T): T | undefined { + let p = this.tree.root; + let higher = null; + while (p) { + if (this.compare(val, p.data) < 0) { + higher = p; + p = p.left; + } else { + p = p.right; + } + } + return higher?.data; + } + + lower(val: T): T | undefined { + let p = this.tree.root; + let lower = null; + while (p) { + if (this.compare(p.data, val) < 0) { + lower = p; + p = p.right; + } else { + p = p.left; + } + } + return lower?.data; + } + + first(): T | undefined { + return this.tree.inOrder().next().value; + } + + last(): T | undefined { + return this.tree.reverseInOrder().next().value; + } + + shift(): T | undefined { + const first = this.first(); + if (first === undefined) return undefined; + this.delete(first); + return first; + } + + pop(): T | undefined { + const last = this.last(); + if (last === undefined) return undefined; + this.delete(last); + return last; + } + + *[Symbol.iterator](): Generator { + yield* this.values(); + } + + *keys(): Generator { + for (const val of this.values()) yield val; + } + + *values(): Generator { + for (const val of this.tree.inOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } + + /** + * Return a generator for reverse order traversing the multi-set + */ + *rvalues(): Generator { + for (const val of this.tree.reverseInOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } +} diff --git a/solution/2800-2899/2818.Apply Operations to Maximize Score/README.md b/solution/2800-2899/2818.Apply Operations to Maximize Score/README.md index 51ec006a98d88..b684a90136951 100644 --- a/solution/2800-2899/2818.Apply Operations to Maximize Score/README.md +++ b/solution/2800-2899/2818.Apply Operations to Maximize Score/README.md @@ -64,6 +64,12 @@ **方法一:单调栈 + 排序贪心** +我们不妨考虑枚举每个元素 $nums[i]$ 作为质数分数最高的元素,那么我们需要找出左边第一个质数分数大于等于当前元素的位置 $l$,以及右边第一个质数分数大于当前元素的位置 $r$,那么以当前元素为最高质数分数的子数组有 $cnt = (i - l) \times (r - i)$ 个,它对答案的贡献为 $nums[i]^{cnt}$。 + +而题目限制了最多进行 $k$ 次操作,我们可以贪心地从大到小枚举 $nums[i]$,每一次算出其子数组的个数 $cnt$,如果 $cnt \le k$,那么 $nums[i]$ 对答案的贡献就是 $nums[i]^{cnt}$,然后我们更新 $k = k - cnt$,继续枚举下一个元素。如果 $cnt \gt k$,那么 $nums[i]$ 对答案的贡献就是 $nums[i]^{k}$,然后退出循环。 + +枚举结束,返回答案即可。注意,由于幂次较大,我们需要用到快速幂。 + 时间复杂度 $O(n \times \log n)$,空间复杂度 $O(n)$。其中 $n$ 是数组的长度。 @@ -129,19 +135,332 @@ class Solution: ```java - +class Solution { + private final int mod = (int) 1e9 + 7; + + public int maximumScore(List nums, int k) { + int n = nums.size(); + int[][] arr = new int[n][0]; + for (int i = 0; i < n; ++i) { + arr[i] = new int[]{i, primeFactors(nums.get(i)), nums.get(i)}; + } + int[] left = new int[n]; + int[] right = new int[n]; + Arrays.fill(left, -1); + Arrays.fill(right, n); + Deque stk = new ArrayDeque<>(); + for (int[] e : arr) { + int i = e[0], f = e[1]; + while (!stk.isEmpty() && arr[stk.peek()][1] < f) { + stk.pop(); + } + if (!stk.isEmpty()) { + left[i] = stk.peek(); + } + stk.push(i); + } + stk.clear(); + for (int i = n - 1; i >= 0; --i) { + int f = arr[i][1]; + while (!stk.isEmpty() && arr[stk.peek()][1] <= f) { + stk.pop(); + } + if (!stk.isEmpty()) { + right[i] = stk.peek(); + } + stk.push(i); + } + Arrays.sort(arr, (a, b) -> b[2] - a[2]); + long ans = 1; + for (int[] e : arr) { + int i = e[0], x = e[2]; + int l = left[i], r = right[i]; + long cnt = (long) (i - l) * (r - i); + if (cnt <= k) { + ans = ans * qmi(x, cnt, mod) % mod; + k -= cnt; + } else { + ans = ans * qmi(x, k, mod) % mod; + break; + } + } + return (int) ans; + } + + private int primeFactors(int n) { + int i = 2; + Set ans = new HashSet<>(); + while (i <= n / i) { + while (n % i == 0) { + ans.add(i); + n /= i; + } + ++i; + } + if (n > 1) { + ans.add(n); + } + return ans.size(); + } + + private int qmi(long a, long k, int p) { + long res = 1; + while (k != 0) { + if ((k & 1) == 1) { + res = res * a % p; + } + k >>= 1; + a = a * a % p; + } + return (int) res; + } +} ``` ### **C++** ```cpp - +class Solution { +public: + int maximumScore(vector& nums, int k) { + const int mod = 1e9 + 7; + int n = nums.size(); + vector> arr(n); + for (int i = 0; i < n; ++i) { + arr[i] = {i, primeFactors(nums[i]), nums[i]}; + } + vector left(n, -1); + vector right(n, n); + stack stk; + for (auto [i, f, _] : arr) { + while (!stk.empty() && get<1>(arr[stk.top()]) < f) { + stk.pop(); + } + if (!stk.empty()) { + left[i] = stk.top(); + } + stk.push(i); + } + stk = stack(); + for (int i = n - 1; ~i; --i) { + int f = get<1>(arr[i]); + while (!stk.empty() && get<1>(arr[stk.top()]) <= f) { + stk.pop(); + } + if (!stk.empty()) { + right[i] = stk.top(); + } + stk.push(i); + } + sort(arr.begin(), arr.end(), [](const auto& lhs, const auto& rhs) { + return get<2>(rhs) < get<2>(lhs); + }); + long long ans = 1; + for (auto [i, _, x] : arr) { + int l = left[i], r = right[i]; + long long cnt = 1LL * (i - l) * (r - i); + if (cnt <= k) { + ans = ans * qmi(x, cnt, mod) % mod; + k -= cnt; + } else { + ans = ans * qmi(x, k, mod) % mod; + break; + } + } + return ans; + } + + int primeFactors(int n) { + int i = 2; + unordered_set ans; + while (i <= n / i) { + while (n % i == 0) { + ans.insert(i); + n /= i; + } + ++i; + } + if (n > 1) { + ans.insert(n); + } + return ans.size(); + } + + long qmi(long a, long k, long p) { + long res = 1; + while (k != 0) { + if ((k & 1) == 1) { + res = res * a % p; + } + k >>= 1; + a = a * a % p; + } + return res; + } +}; ``` ### **Go** ```go +func maximumScore(nums []int, k int) int { + n := len(nums) + const mod = 1e9 + 7 + arr := make([][3]int, n) + left := make([]int, n) + right := make([]int, n) + for i, x := range nums { + left[i] = -1 + right[i] = n + arr[i] = [3]int{i, primeFactors(x), x} + } + stk := []int{} + for _, e := range arr { + i, f := e[0], e[1] + for len(stk) > 0 && arr[stk[len(stk)-1]][1] < f { + stk = stk[:len(stk)-1] + } + if len(stk) > 0 { + left[i] = stk[len(stk)-1] + } + stk = append(stk, i) + } + stk = []int{} + for i := n - 1; i >= 0; i-- { + f := arr[i][1] + for len(stk) > 0 && arr[stk[len(stk)-1]][1] <= f { + stk = stk[:len(stk)-1] + } + if len(stk) > 0 { + right[i] = stk[len(stk)-1] + } + stk = append(stk, i) + } + sort.Slice(arr, func(i, j int) bool { return arr[i][2] > arr[j][2] }) + ans := 1 + for _, e := range arr { + i, x := e[0], e[2] + l, r := left[i], right[i] + cnt := (i - l) * (r - i) + if cnt <= k { + ans = ans * qmi(x, cnt, mod) % mod + k -= cnt + } else { + ans = ans * qmi(x, k, mod) % mod + break + } + } + return ans +} + +func primeFactors(n int) int { + i := 2 + ans := map[int]bool{} + for i <= n/i { + for n%i == 0 { + ans[i] = true + n /= i + } + i++ + } + if n > 1 { + ans[n] = true + } + return len(ans) +} + +func qmi(a, k, p int) int { + res := 1 + for k != 0 { + if k&1 == 1 { + res = res * a % p + } + k >>= 1 + a = a * a % p + } + return res +} +``` +### **TypeScript** + +```ts +function maximumScore(nums: number[], k: number): number { + const mod = 10 ** 9 + 7; + const n = nums.length; + const arr: number[][] = Array(n) + .fill(0) + .map(() => Array(3).fill(0)); + const left: number[] = Array(n).fill(-1); + const right: number[] = Array(n).fill(n); + for (let i = 0; i < n; ++i) { + arr[i] = [i, primeFactors(nums[i]), nums[i]]; + } + const stk: number[] = []; + for (const [i, f, _] of arr) { + while (stk.length && arr[stk.at(-1)!][1] < f) { + stk.pop(); + } + if (stk.length) { + left[i] = stk.at(-1)!; + } + stk.push(i); + } + stk.length = 0; + for (let i = n - 1; i >= 0; --i) { + const f = arr[i][1]; + while (stk.length && arr[stk.at(-1)!][1] <= f) { + stk.pop(); + } + if (stk.length) { + right[i] = stk.at(-1)!; + } + stk.push(i); + } + arr.sort((a, b) => b[2] - a[2]); + let ans = 1n; + for (const [i, _, x] of arr) { + const l = left[i]; + const r = right[i]; + const cnt = (i - l) * (r - i); + if (cnt <= k) { + ans = (ans * qmi(BigInt(x), cnt, BigInt(mod))) % BigInt(mod); + k -= cnt; + } else { + ans = (ans * qmi(BigInt(x), k, BigInt(mod))) % BigInt(mod); + break; + } + } + return Number(ans); +} + +function primeFactors(n: number): number { + let i = 2; + const s: Set = new Set(); + while (i * i <= n) { + while (n % i === 0) { + s.add(i); + n = Math.floor(n / i); + } + ++i; + } + if (n > 1) { + s.add(n); + } + return s.size; +} + +function qmi(a: bigint, k: number, p: bigint): bigint { + let res = 1n; + while (k) { + if ((k & 1) === 1) { + res = (res * a) % p; + } + k >>= 1; + a = (a * a) % p; + } + return res; +} ``` ### **...** diff --git a/solution/2800-2899/2818.Apply Operations to Maximize Score/README_EN.md b/solution/2800-2899/2818.Apply Operations to Maximize Score/README_EN.md index 5c2cf2d908552..a1ac1dac4e9f8 100644 --- a/solution/2800-2899/2818.Apply Operations to Maximize Score/README_EN.md +++ b/solution/2800-2899/2818.Apply Operations to Maximize Score/README_EN.md @@ -56,6 +56,16 @@ It can be proven that 4788 is the highest score one can obtain. ## Solutions +**Solution 1: Monotonic Stack + Greedy** + +It is not difficult to see that the number of subarrays with the highest prime score of an element $nums[i]$ is $cnt = (i - l) \times (r - i)$, where $l$ is the leftmost index such that $primeScore(nums[l]) \ge primeScore(nums[i])$, and $r$ is the rightmost index such that $primeScore(nums[r]) \ge primeScore(nums[i])$. + +Since we are allowed to operate at most $k$ times, we can greedily enumerate $nums[i]$ from large to small, and compute the $cnt$ of each element. If $cnt \le k$, then the contribution of $nums[i]$ to the answer is $nums[i]^{cnt}$, and we update $k = k - cnt$. If $cnt \gt k$, then the contribution of $nums[i]$ to the answer is $nums[i]^{k}$, and we break out the loop. + +Return the answer after the loop. Note that the power is large, so we need to use fast power. + +The time complexity is $O(n \times \log n)$, and the space complexity is $O(n)$. Where $n$ is the length of the array. + ### **Python3** @@ -115,19 +125,332 @@ class Solution: ### **Java** ```java - +class Solution { + private final int mod = (int) 1e9 + 7; + + public int maximumScore(List nums, int k) { + int n = nums.size(); + int[][] arr = new int[n][0]; + for (int i = 0; i < n; ++i) { + arr[i] = new int[]{i, primeFactors(nums.get(i)), nums.get(i)}; + } + int[] left = new int[n]; + int[] right = new int[n]; + Arrays.fill(left, -1); + Arrays.fill(right, n); + Deque stk = new ArrayDeque<>(); + for (int[] e : arr) { + int i = e[0], f = e[1]; + while (!stk.isEmpty() && arr[stk.peek()][1] < f) { + stk.pop(); + } + if (!stk.isEmpty()) { + left[i] = stk.peek(); + } + stk.push(i); + } + stk.clear(); + for (int i = n - 1; i >= 0; --i) { + int f = arr[i][1]; + while (!stk.isEmpty() && arr[stk.peek()][1] <= f) { + stk.pop(); + } + if (!stk.isEmpty()) { + right[i] = stk.peek(); + } + stk.push(i); + } + Arrays.sort(arr, (a, b) -> b[2] - a[2]); + long ans = 1; + for (int[] e : arr) { + int i = e[0], x = e[2]; + int l = left[i], r = right[i]; + long cnt = (long) (i - l) * (r - i); + if (cnt <= k) { + ans = ans * qmi(x, cnt, mod) % mod; + k -= cnt; + } else { + ans = ans * qmi(x, k, mod) % mod; + break; + } + } + return (int) ans; + } + + private int primeFactors(int n) { + int i = 2; + Set ans = new HashSet<>(); + while (i <= n / i) { + while (n % i == 0) { + ans.add(i); + n /= i; + } + ++i; + } + if (n > 1) { + ans.add(n); + } + return ans.size(); + } + + private int qmi(long a, long k, int p) { + long res = 1; + while (k != 0) { + if ((k & 1) == 1) { + res = res * a % p; + } + k >>= 1; + a = a * a % p; + } + return (int) res; + } +} ``` ### **C++** ```cpp - +class Solution { +public: + int maximumScore(vector& nums, int k) { + const int mod = 1e9 + 7; + int n = nums.size(); + vector> arr(n); + for (int i = 0; i < n; ++i) { + arr[i] = {i, primeFactors(nums[i]), nums[i]}; + } + vector left(n, -1); + vector right(n, n); + stack stk; + for (auto [i, f, _] : arr) { + while (!stk.empty() && get<1>(arr[stk.top()]) < f) { + stk.pop(); + } + if (!stk.empty()) { + left[i] = stk.top(); + } + stk.push(i); + } + stk = stack(); + for (int i = n - 1; ~i; --i) { + int f = get<1>(arr[i]); + while (!stk.empty() && get<1>(arr[stk.top()]) <= f) { + stk.pop(); + } + if (!stk.empty()) { + right[i] = stk.top(); + } + stk.push(i); + } + sort(arr.begin(), arr.end(), [](const auto& lhs, const auto& rhs) { + return get<2>(rhs) < get<2>(lhs); + }); + long long ans = 1; + for (auto [i, _, x] : arr) { + int l = left[i], r = right[i]; + long long cnt = 1LL * (i - l) * (r - i); + if (cnt <= k) { + ans = ans * qmi(x, cnt, mod) % mod; + k -= cnt; + } else { + ans = ans * qmi(x, k, mod) % mod; + break; + } + } + return ans; + } + + int primeFactors(int n) { + int i = 2; + unordered_set ans; + while (i <= n / i) { + while (n % i == 0) { + ans.insert(i); + n /= i; + } + ++i; + } + if (n > 1) { + ans.insert(n); + } + return ans.size(); + } + + long qmi(long a, long k, long p) { + long res = 1; + while (k != 0) { + if ((k & 1) == 1) { + res = res * a % p; + } + k >>= 1; + a = a * a % p; + } + return res; + } +}; ``` ### **Go** ```go +func maximumScore(nums []int, k int) int { + n := len(nums) + const mod = 1e9 + 7 + arr := make([][3]int, n) + left := make([]int, n) + right := make([]int, n) + for i, x := range nums { + left[i] = -1 + right[i] = n + arr[i] = [3]int{i, primeFactors(x), x} + } + stk := []int{} + for _, e := range arr { + i, f := e[0], e[1] + for len(stk) > 0 && arr[stk[len(stk)-1]][1] < f { + stk = stk[:len(stk)-1] + } + if len(stk) > 0 { + left[i] = stk[len(stk)-1] + } + stk = append(stk, i) + } + stk = []int{} + for i := n - 1; i >= 0; i-- { + f := arr[i][1] + for len(stk) > 0 && arr[stk[len(stk)-1]][1] <= f { + stk = stk[:len(stk)-1] + } + if len(stk) > 0 { + right[i] = stk[len(stk)-1] + } + stk = append(stk, i) + } + sort.Slice(arr, func(i, j int) bool { return arr[i][2] > arr[j][2] }) + ans := 1 + for _, e := range arr { + i, x := e[0], e[2] + l, r := left[i], right[i] + cnt := (i - l) * (r - i) + if cnt <= k { + ans = ans * qmi(x, cnt, mod) % mod + k -= cnt + } else { + ans = ans * qmi(x, k, mod) % mod + break + } + } + return ans +} + +func primeFactors(n int) int { + i := 2 + ans := map[int]bool{} + for i <= n/i { + for n%i == 0 { + ans[i] = true + n /= i + } + i++ + } + if n > 1 { + ans[n] = true + } + return len(ans) +} + +func qmi(a, k, p int) int { + res := 1 + for k != 0 { + if k&1 == 1 { + res = res * a % p + } + k >>= 1 + a = a * a % p + } + return res +} +``` +### **TypeScript** + +```ts +function maximumScore(nums: number[], k: number): number { + const mod = 10 ** 9 + 7; + const n = nums.length; + const arr: number[][] = Array(n) + .fill(0) + .map(() => Array(3).fill(0)); + const left: number[] = Array(n).fill(-1); + const right: number[] = Array(n).fill(n); + for (let i = 0; i < n; ++i) { + arr[i] = [i, primeFactors(nums[i]), nums[i]]; + } + const stk: number[] = []; + for (const [i, f, _] of arr) { + while (stk.length && arr[stk.at(-1)!][1] < f) { + stk.pop(); + } + if (stk.length) { + left[i] = stk.at(-1)!; + } + stk.push(i); + } + stk.length = 0; + for (let i = n - 1; i >= 0; --i) { + const f = arr[i][1]; + while (stk.length && arr[stk.at(-1)!][1] <= f) { + stk.pop(); + } + if (stk.length) { + right[i] = stk.at(-1)!; + } + stk.push(i); + } + arr.sort((a, b) => b[2] - a[2]); + let ans = 1n; + for (const [i, _, x] of arr) { + const l = left[i]; + const r = right[i]; + const cnt = (i - l) * (r - i); + if (cnt <= k) { + ans = (ans * qmi(BigInt(x), cnt, BigInt(mod))) % BigInt(mod); + k -= cnt; + } else { + ans = (ans * qmi(BigInt(x), k, BigInt(mod))) % BigInt(mod); + break; + } + } + return Number(ans); +} + +function primeFactors(n: number): number { + let i = 2; + const s: Set = new Set(); + while (i * i <= n) { + while (n % i === 0) { + s.add(i); + n = Math.floor(n / i); + } + ++i; + } + if (n > 1) { + s.add(n); + } + return s.size; +} + +function qmi(a: bigint, k: number, p: bigint): bigint { + let res = 1n; + while (k) { + if ((k & 1) === 1) { + res = (res * a) % p; + } + k >>= 1; + a = (a * a) % p; + } + return res; +} ``` ### **...** diff --git a/solution/2800-2899/2818.Apply Operations to Maximize Score/Solution.cpp b/solution/2800-2899/2818.Apply Operations to Maximize Score/Solution.cpp new file mode 100644 index 0000000000000..b3cedc4b3608a --- /dev/null +++ b/solution/2800-2899/2818.Apply Operations to Maximize Score/Solution.cpp @@ -0,0 +1,78 @@ +class Solution { +public: + int maximumScore(vector& nums, int k) { + const int mod = 1e9 + 7; + int n = nums.size(); + vector> arr(n); + for (int i = 0; i < n; ++i) { + arr[i] = {i, primeFactors(nums[i]), nums[i]}; + } + vector left(n, -1); + vector right(n, n); + stack stk; + for (auto [i, f, _] : arr) { + while (!stk.empty() && get<1>(arr[stk.top()]) < f) { + stk.pop(); + } + if (!stk.empty()) { + left[i] = stk.top(); + } + stk.push(i); + } + stk = stack(); + for (int i = n - 1; ~i; --i) { + int f = get<1>(arr[i]); + while (!stk.empty() && get<1>(arr[stk.top()]) <= f) { + stk.pop(); + } + if (!stk.empty()) { + right[i] = stk.top(); + } + stk.push(i); + } + sort(arr.begin(), arr.end(), [](const auto& lhs, const auto& rhs) { + return get<2>(rhs) < get<2>(lhs); + }); + long long ans = 1; + for (auto [i, _, x] : arr) { + int l = left[i], r = right[i]; + long long cnt = 1LL * (i - l) * (r - i); + if (cnt <= k) { + ans = ans * qmi(x, cnt, mod) % mod; + k -= cnt; + } else { + ans = ans * qmi(x, k, mod) % mod; + break; + } + } + return ans; + } + + int primeFactors(int n) { + int i = 2; + unordered_set ans; + while (i <= n / i) { + while (n % i == 0) { + ans.insert(i); + n /= i; + } + ++i; + } + if (n > 1) { + ans.insert(n); + } + return ans.size(); + } + + long qmi(long a, long k, long p) { + long res = 1; + while (k != 0) { + if ((k & 1) == 1) { + res = res * a % p; + } + k >>= 1; + a = a * a % p; + } + return res; + } +}; \ No newline at end of file diff --git a/solution/2800-2899/2818.Apply Operations to Maximize Score/Solution.go b/solution/2800-2899/2818.Apply Operations to Maximize Score/Solution.go new file mode 100644 index 0000000000000..4edd82aa11f28 --- /dev/null +++ b/solution/2800-2899/2818.Apply Operations to Maximize Score/Solution.go @@ -0,0 +1,77 @@ +func maximumScore(nums []int, k int) int { + n := len(nums) + const mod = 1e9 + 7 + arr := make([][3]int, n) + left := make([]int, n) + right := make([]int, n) + for i, x := range nums { + left[i] = -1 + right[i] = n + arr[i] = [3]int{i, primeFactors(x), x} + } + stk := []int{} + for _, e := range arr { + i, f := e[0], e[1] + for len(stk) > 0 && arr[stk[len(stk)-1]][1] < f { + stk = stk[:len(stk)-1] + } + if len(stk) > 0 { + left[i] = stk[len(stk)-1] + } + stk = append(stk, i) + } + stk = []int{} + for i := n - 1; i >= 0; i-- { + f := arr[i][1] + for len(stk) > 0 && arr[stk[len(stk)-1]][1] <= f { + stk = stk[:len(stk)-1] + } + if len(stk) > 0 { + right[i] = stk[len(stk)-1] + } + stk = append(stk, i) + } + sort.Slice(arr, func(i, j int) bool { return arr[i][2] > arr[j][2] }) + ans := 1 + for _, e := range arr { + i, x := e[0], e[2] + l, r := left[i], right[i] + cnt := (i - l) * (r - i) + if cnt <= k { + ans = ans * qmi(x, cnt, mod) % mod + k -= cnt + } else { + ans = ans * qmi(x, k, mod) % mod + break + } + } + return ans +} + +func primeFactors(n int) int { + i := 2 + ans := map[int]bool{} + for i <= n/i { + for n%i == 0 { + ans[i] = true + n /= i + } + i++ + } + if n > 1 { + ans[n] = true + } + return len(ans) +} + +func qmi(a, k, p int) int { + res := 1 + for k != 0 { + if k&1 == 1 { + res = res * a % p + } + k >>= 1 + a = a * a % p + } + return res +} \ No newline at end of file diff --git a/solution/2800-2899/2818.Apply Operations to Maximize Score/Solution.java b/solution/2800-2899/2818.Apply Operations to Maximize Score/Solution.java new file mode 100644 index 0000000000000..326984cb56f1a --- /dev/null +++ b/solution/2800-2899/2818.Apply Operations to Maximize Score/Solution.java @@ -0,0 +1,80 @@ +class Solution { + private final int mod = (int) 1e9 + 7; + + public int maximumScore(List nums, int k) { + int n = nums.size(); + int[][] arr = new int[n][0]; + for (int i = 0; i < n; ++i) { + arr[i] = new int[] {i, primeFactors(nums.get(i)), nums.get(i)}; + } + int[] left = new int[n]; + int[] right = new int[n]; + Arrays.fill(left, -1); + Arrays.fill(right, n); + Deque stk = new ArrayDeque<>(); + for (int[] e : arr) { + int i = e[0], f = e[1]; + while (!stk.isEmpty() && arr[stk.peek()][1] < f) { + stk.pop(); + } + if (!stk.isEmpty()) { + left[i] = stk.peek(); + } + stk.push(i); + } + stk.clear(); + for (int i = n - 1; i >= 0; --i) { + int f = arr[i][1]; + while (!stk.isEmpty() && arr[stk.peek()][1] <= f) { + stk.pop(); + } + if (!stk.isEmpty()) { + right[i] = stk.peek(); + } + stk.push(i); + } + Arrays.sort(arr, (a, b) -> b[2] - a[2]); + long ans = 1; + for (int[] e : arr) { + int i = e[0], x = e[2]; + int l = left[i], r = right[i]; + long cnt = (long) (i - l) * (r - i); + if (cnt <= k) { + ans = ans * qmi(x, cnt, mod) % mod; + k -= cnt; + } else { + ans = ans * qmi(x, k, mod) % mod; + break; + } + } + return (int) ans; + } + + private int primeFactors(int n) { + int i = 2; + Set ans = new HashSet<>(); + while (i <= n / i) { + while (n % i == 0) { + ans.add(i); + n /= i; + } + ++i; + } + if (n > 1) { + ans.add(n); + } + return ans.size(); + } + + private int qmi(long a, long k, int p) { + long res = 1; + while (k != 0) { + if ((k & 1) == 1) { + res = res * a % p; + } + k >>= 1; + a = a * a % p; + } + return (int) res; + } +} \ No newline at end of file diff --git a/solution/2800-2899/2818.Apply Operations to Maximize Score/Solution.ts b/solution/2800-2899/2818.Apply Operations to Maximize Score/Solution.ts new file mode 100644 index 0000000000000..a01226eed2ed7 --- /dev/null +++ b/solution/2800-2899/2818.Apply Operations to Maximize Score/Solution.ts @@ -0,0 +1,76 @@ +function maximumScore(nums: number[], k: number): number { + const mod = 10 ** 9 + 7; + const n = nums.length; + const arr: number[][] = Array(n) + .fill(0) + .map(() => Array(3).fill(0)); + const left: number[] = Array(n).fill(-1); + const right: number[] = Array(n).fill(n); + for (let i = 0; i < n; ++i) { + arr[i] = [i, primeFactors(nums[i]), nums[i]]; + } + const stk: number[] = []; + for (const [i, f, _] of arr) { + while (stk.length && arr[stk.at(-1)!][1] < f) { + stk.pop(); + } + if (stk.length) { + left[i] = stk.at(-1)!; + } + stk.push(i); + } + stk.length = 0; + for (let i = n - 1; i >= 0; --i) { + const f = arr[i][1]; + while (stk.length && arr[stk.at(-1)!][1] <= f) { + stk.pop(); + } + if (stk.length) { + right[i] = stk.at(-1)!; + } + stk.push(i); + } + arr.sort((a, b) => b[2] - a[2]); + let ans = 1n; + for (const [i, _, x] of arr) { + const l = left[i]; + const r = right[i]; + const cnt = (i - l) * (r - i); + if (cnt <= k) { + ans = (ans * qmi(BigInt(x), cnt, BigInt(mod))) % BigInt(mod); + k -= cnt; + } else { + ans = (ans * qmi(BigInt(x), k, BigInt(mod))) % BigInt(mod); + break; + } + } + return Number(ans); +} + +function primeFactors(n: number): number { + let i = 2; + const s: Set = new Set(); + while (i * i <= n) { + while (n % i === 0) { + s.add(i); + n = Math.floor(n / i); + } + ++i; + } + if (n > 1) { + s.add(n); + } + return s.size; +} + +function qmi(a: bigint, k: number, p: bigint): bigint { + let res = 1n; + while (k) { + if ((k & 1) === 1) { + res = (res * a) % p; + } + k >>= 1; + a = (a * a) % p; + } + return res; +}