Elliot the Hawk has a very important task − the breeding season has come. He needs to prepare a nest in which his mating partner Eleonora will lay eggs, then look after her until they switch responsibility for taking care of the eggs.
There are N birds' nests standing in a line, one after another. Every two nests are positioned at distinct heights.
Elliot needs to pick a nest in which Eleonora will lay her eggs. Then, each day, Elliot will hunt from one of the other nests. At the end of the day, he will bring the food to Eleonora and move to some other nest on the opposite side of their own nest. He can hunt from each nest at most once. Moreover, on each day he needs to hunt from a nest that is positioned higher than the previous nest, and higher than their own nest.
Elliot can return to their own nest and switch roles with Eleonora at any time. In particular, he may simply prepare their nest, then refrain from hunting at all.
For example, assume the nests are positioned at heights 4 6 2 1 5. Elliot can, for instance, prepare the nest at height 1, then hunt from the nest at height 4, on the next day hunt from the nest at height 5, and on the last day hunt from the nest at height 6:
Note that in this situation, Elliot cannot hunt from the nest at height 6 just after hunting from the nest at height 4, because these two nests are on the same side of his own nest. Also, after hunting from the rightmost nest, he cannot hunt from the leftmost nest, as it is lower than the previous one. If Elliot chooses the nest at height 6 as their own nest, then he cannot hunt at all, because all other nests are placed lower.
Elliot wonders how many possible ways there are for him to choose their nest and then hunt until he changes places with Eleonora. As the result may be very large, count the number of possibilities modulo 109 + 7 (1,000,000,007).
Write a function:
class Solution { public int solution(int[] H); }
that, given a sequence of heights of nests, returns the remainder from the division by 1,000,000,007 of the number of possible ways for Elliot to choose the nest and hunt.
For example, given:
H = [ 13, 2, 5 ]the function should return 7. All the possible ways for Elliot to choose the nest and hunt are as follows, listed one per line. The first number in each line denotes the height of Elliot's own nest, and each consecutive number describes the height of a nest he will hunt from.
13 5 13 5 2 5 13 2 5 2 13 2On the other hand, for the following array:
H = [ 4, 6, 2, 1, 5 ]the function should return 23. One of the possible ways Elliot can act is depicted in the figure above.
Write an efficient algorithm for the following assumptions:
- N is an integer within the range [1..50,000];
- each element of array H is an integer within the range [1..1,000,000,000];
- the elements of H are all distinct.
//
// Chromium.swift
// Codility
//
// Created by Werner Altewischer on 1/21/20.
//
import Foundation
private let MOD: Int64 = 1_000_000_007
// Define convenience operators to take the modulus into account for additions and multiplications
infix operator +% : AdditionPrecedence
infix operator *% : MultiplicationPrecedence
infix operator +%= : AssignmentPrecedence
extension Int64 {
fileprivate static func +%(lhs: Int64, rhs: Int64) -> Int64 {
return (lhs &+ rhs) % MOD
}
fileprivate static func *%(lhs: Int64, rhs: Int64) -> Int64 {
return (lhs &* rhs) % MOD
}
fileprivate static func +%= (lhs: inout Int64, rhs: Int64) {
lhs = (lhs &+ rhs) % MOD
}
}
extension ClosedRange {
func containsFully(_ other: ClosedRange) -> Bool {
return self.lowerBound <= other.lowerBound && self.upperBound >= other.upperBound
}
}
private struct Nest {
let index: Int
let height: Int
}
private final class Node {
init(left: Int64 = 0, right: Int64 = 0, total: Int64 = 0) {
self.left = left
self.right = right
}
var right: Int64
var left: Int64
var isProcessed: Bool {
return left != 0 || right != 0
}
func merge(_ first: Node, _ second: Node) {
self.left = first.left +% second.left
self.right = first.right +% second.right
}
}
private final class LazyNode {
var ll: Int64 = 0
var rl: Int64 = 0
var lr: Int64 = 0
var rr: Int64 = 0
var isFilled: Bool {
return ll != 0 || rr != 0 || lr != 0 || rl != 0
}
func reset() {
ll = 0
lr = 0
rl = 0
rr = 0
}
func update(parent: LazyNode) {
let deltaRR = parent.rr &+ parent.rr *% self.rr &+ parent.lr *% self.rl
let deltaLR = parent.lr &+ parent.lr *% self.ll &+ self.lr *% parent.rr
let deltaLL = parent.ll &+ parent.ll *% self.ll &+ parent.rl *% self.lr
let deltaRL = parent.rl &+ parent.rl *% self.rr &+ self.rl *% parent.ll
self.rr +%= deltaRR
self.lr +%= deltaLR
self.ll +%= deltaLL
self.rl +%= deltaRL
}
}
private enum ProcessMode {
case left, right, initial;
}
private final class LazySegmentTree {
private let tree: [Node]
private let lazy: [LazyNode]
private let length: Int
init(length: Int) {
var pow2 = 1
while pow2 < length {
pow2 <<= 1
}
//Maximum size of segment tree
let maxTreeSize = 2 * pow2 - 1;
var nodes = [Node]()
nodes.reserveCapacity(maxTreeSize)
var lazyNodes = [LazyNode]()
lazyNodes.reserveCapacity(maxTreeSize)
for _ in 0..<maxTreeSize {
nodes.append(Node())
lazyNodes.append(LazyNode())
}
self.length = length
self.tree = nodes
self.lazy = lazyNodes
}
private func processRangeUpdate(nodeIndex: Int, range: ClosedRange<Int>, queryRange: ClosedRange<Int>, processMode: ProcessMode) {
// out of range
if !range.overlaps(queryRange) {
return
}
let lazyNode = lazy[nodeIndex]
let treeNode = tree[nodeIndex]
let firstChildIndex = 2 * nodeIndex + 1
let secondChildIndex = 2 * nodeIndex + 2
if lazyNode.isFilled {
let deltaLeft = lazyNode.rl *% treeNode.right + lazyNode.ll *% treeNode.left
let deltaRight = lazyNode.rr *% treeNode.right + lazyNode.lr *% treeNode.left
treeNode.left +%= deltaLeft
treeNode.right +%= deltaRight
// leaf node check
if range.count != 1 {
// Propagate Values to lazy children...
addToLazyNode(childIndex: firstChildIndex, parent: lazyNode)
addToLazyNode(childIndex: secondChildIndex, parent: lazyNode)
}
// because the update is made, set lazy values from this node to 0
lazyNode.reset()
}
// Current segment is fully in range
if queryRange.containsFully(range) {
switch processMode {
case .left:
treeNode.left +%= treeNode.right
case .right:
treeNode.right +%= treeNode.left
case .initial:
treeNode.left = 1
treeNode.right = 1
}
// leaf node check
if range.count != 1 {
// When Update R: LR += 1 + LL; RR += RL
// When Update L: RL += 1 + RR; LL += LR;
switch processMode {
case .left:
if tree[firstChildIndex].isProcessed {
lazy[firstChildIndex].rl +%= 1 &+ lazy[firstChildIndex].rr
lazy[firstChildIndex].ll +%= lazy[firstChildIndex].lr
}
if tree[secondChildIndex].isProcessed {
lazy[secondChildIndex].rl +%= 1 &+ lazy[secondChildIndex].rr
lazy[secondChildIndex].ll +%= lazy[secondChildIndex].lr
}
case .right:
if tree[firstChildIndex].isProcessed {
lazy[firstChildIndex].lr +%= 1 &+ lazy[firstChildIndex].ll
lazy[firstChildIndex].rr +%= lazy[firstChildIndex].rl
}
if tree[secondChildIndex].isProcessed {
lazy[secondChildIndex].lr +%= 1 &+ lazy[secondChildIndex].ll
lazy[secondChildIndex].rr +%= lazy[secondChildIndex].rl
}
default:
break
}
}
// because the processing segment is fully in range - return
return
}
// not completely in range (but overlaps) process children
let mid = (range.lowerBound + range.upperBound) / 2
processRangeUpdate(nodeIndex: firstChildIndex, range: range.lowerBound...mid, queryRange: queryRange, processMode: processMode)
processRangeUpdate(nodeIndex: secondChildIndex, range: mid+1...range.upperBound, queryRange: queryRange, processMode: processMode)
//Update this node with the results of the children
tree[nodeIndex].merge(tree[firstChildIndex], tree[secondChildIndex])
}
private func updateRange(queryRange: ClosedRange<Int>, processMode: ProcessMode) {
processRangeUpdate(nodeIndex: 0, range: 0...length-1, queryRange: queryRange, processMode: processMode)
}
private func processSumInRange(range: ClosedRange<Int>, queryRange: ClosedRange<Int>, nodeIndex: Int, processMode: ProcessMode) -> Int64 {
let firstChildIndex = 2 * nodeIndex + 1
let secondChildIndex = 2 * nodeIndex + 2
if lazy[nodeIndex].isFilled {
//// update tree[si] according to RR,LR,RL,LL
let deltaLeft = lazy[nodeIndex].rl *% tree[nodeIndex].right &+ lazy[nodeIndex].ll *% tree[nodeIndex].left
let deltaRight = lazy[nodeIndex].rr *% tree[nodeIndex].right &+ lazy[nodeIndex].lr *% tree[nodeIndex].left
tree[nodeIndex].left +%= deltaLeft
tree[nodeIndex].right +%= deltaRight
// leaf node check
if range.count != 1 {
// propagate lazy data to child nodes
addToLazyNode(childIndex: firstChildIndex, parent: lazy[nodeIndex])
addToLazyNode(childIndex: secondChildIndex, parent: lazy[nodeIndex])
}
// lazy data is processed, set values to 0
lazy[nodeIndex].reset()
}
if range.isEmpty || !range.overlaps(queryRange) {
return 0
}
// this segment completely lies in range
if queryRange.containsFully(range) {
switch processMode {
case .left:
return tree[nodeIndex].left
case .right:
return tree[nodeIndex].right
default:
return 0
}
}
// part of this segment overlaps with query range
let mid = (range.lowerBound + range.upperBound) / 2;
return processSumInRange(range: range.lowerBound...mid, queryRange: queryRange, nodeIndex: firstChildIndex, processMode: processMode) +%
processSumInRange(range: mid+1...range.upperBound, queryRange: queryRange, nodeIndex: secondChildIndex, processMode: processMode)
}
private func addToLazyNode(childIndex: Int, parent: LazyNode) {
if !tree[childIndex].isProcessed {
return
}
lazy[childIndex].update(parent: parent)
}
// get sum of elements in range
func sumInRange(queryRange: ClosedRange<Int>, processMode: ProcessMode) -> Int64 {
return processSumInRange(range: 0...length-1, queryRange: queryRange, nodeIndex: 0, processMode: processMode)
}
func processElement(index: Int) -> Int64 {
let sumLeft = index > 0 ? sumInRange(queryRange: 0...index-1, processMode: .left) : 0
let sumRight = index < length - 1 ? sumInRange(queryRange: index+1...length-1, processMode: .right) : 0
let total = (1 &+ sumLeft +% sumRight)
// update current node/nest combinations
updateRange(queryRange: index...index, processMode: .initial)
if (index > 0) {
// update nodes on the left
updateRange(queryRange: 0...index-1, processMode: .right)
}
if (index < length - 1) {
// update nodes on the right
updateRange(queryRange: index+1...length-1, processMode: .left)
}
return total
}
}
public func solution(_ H: inout [Int]) -> Int {
var nests = H.enumerated().map { Nest(index: $0, height: $1) }
nests.sort { $0.height < $1.height }
let tree = LazySegmentTree(length: H.count)
var total: Int64 = 0
for nest in nests {
total +%= tree.processElement(index: nest.index)
}
return Int(total)
}
//
// Chromium.swift
// Codility
//
// Created by Werner Altewischer on 1/21/20.
//
import Foundation
private let MOD: Int64 = 1_000_000_007
// Define convenience operators to take the modulus into account for additions and multiplications
infix operator +% : AdditionPrecedence
infix operator *% : MultiplicationPrecedence
infix operator +%= : AssignmentPrecedence
extension Int64 {
fileprivate static func +%(lhs: Int64, rhs: Int64) -> Int64 {
return (lhs &+ rhs) % MOD
}
fileprivate static func *%(lhs: Int64, rhs: Int64) -> Int64 {
return (lhs &* rhs) % MOD
}
fileprivate static func +%= (lhs: inout Int64, rhs: Int64) {
lhs = (lhs &+ rhs) % MOD
}
}
extension ClosedRange {
func containsFully(_ other: ClosedRange) -> Bool {
return self.lowerBound <= other.lowerBound && self.upperBound >= other.upperBound
}
}
private struct Nest {
let index: Int
let height: Int
}
private final class Node {
init(left: Int64 = 0, right: Int64 = 0, total: Int64 = 0) {
self.left = left
self.right = right
}
var right: Int64
var left: Int64
var isProcessed: Bool {
return left != 0 || right != 0
}
func merge(_ first: Node, _ second: Node) {
self.left = first.left +% second.left
self.right = first.right +% second.right
}
}
private final class LazyNode {
var ll: Int64 = 0
var rl: Int64 = 0
var lr: Int64 = 0
var rr: Int64 = 0
var isFilled: Bool {
return ll != 0 || rr != 0 || lr != 0 || rl != 0
}
func reset() {
ll = 0
lr = 0
rl = 0
rr = 0
}
func update(parent: LazyNode) {
let deltaRR = parent.rr &+ parent.rr *% self.rr &+ parent.lr *% self.rl
let deltaLR = parent.lr &+ parent.lr *% self.ll &+ self.lr *% parent.rr
let deltaLL = parent.ll &+ parent.ll *% self.ll &+ parent.rl *% self.lr
let deltaRL = parent.rl &+ parent.rl *% self.rr &+ self.rl *% parent.ll
self.rr +%= deltaRR
self.lr +%= deltaLR
self.ll +%= deltaLL
self.rl +%= deltaRL
}
}
private enum ProcessMode {
case left, right, initial;
}
private final class LazySegmentTree {
private let tree: [Node]
private let lazy: [LazyNode]
private let length: Int
init(length: Int) {
var pow2 = 1
while pow2 < length {
pow2 <<= 1
}
//Maximum size of segment tree
let maxTreeSize = 2 * pow2 - 1;
var nodes = [Node]()
nodes.reserveCapacity(maxTreeSize)
var lazyNodes = [LazyNode]()
lazyNodes.reserveCapacity(maxTreeSize)
for _ in 0..<maxTreeSize {
nodes.append(Node())
lazyNodes.append(LazyNode())
}
self.length = length
self.tree = nodes
self.lazy = lazyNodes
}
private func processRangeUpdate(nodeIndex: Int, range: ClosedRange<Int>, queryRange: ClosedRange<Int>, processMode: ProcessMode) {
// out of range
if !range.overlaps(queryRange) {
return
}
let lazyNode = lazy[nodeIndex]
let treeNode = tree[nodeIndex]
let firstChildIndex = 2 * nodeIndex + 1
let secondChildIndex = 2 * nodeIndex + 2
if lazyNode.isFilled {
let deltaLeft = lazyNode.rl *% treeNode.right + lazyNode.ll *% treeNode.left
let deltaRight = lazyNode.rr *% treeNode.right + lazyNode.lr *% treeNode.left
treeNode.left +%= deltaLeft
treeNode.right +%= deltaRight
// leaf node check
if range.count != 1 {
// Propagate Values to lazy children...
addToLazyNode(childIndex: firstChildIndex, parent: lazyNode)
addToLazyNode(childIndex: secondChildIndex, parent: lazyNode)
}
// because the update is made, set lazy values from this node to 0
lazyNode.reset()
}
// Current segment is fully in range
if queryRange.containsFully(range) {
switch processMode {
case .left:
treeNode.left +%= treeNode.right
case .right:
treeNode.right +%= treeNode.left
case .initial:
treeNode.left = 1
treeNode.right = 1
}
// leaf node check
if range.count != 1 {
// When Update R: LR += 1 + LL; RR += RL
// When Update L: RL += 1 + RR; LL += LR;
switch processMode {
case .left:
if tree[firstChildIndex].isProcessed {
lazy[firstChildIndex].rl +%= 1 &+ lazy[firstChildIndex].rr
lazy[firstChildIndex].ll +%= lazy[firstChildIndex].lr
}
if tree[secondChildIndex].isProcessed {
lazy[secondChildIndex].rl +%= 1 &+ lazy[secondChildIndex].rr
lazy[secondChildIndex].ll +%= lazy[secondChildIndex].lr
}
case .right:
if tree[firstChildIndex].isProcessed {
lazy[firstChildIndex].lr +%= 1 &+ lazy[firstChildIndex].ll
lazy[firstChildIndex].rr +%= lazy[firstChildIndex].rl
}
if tree[secondChildIndex].isProcessed {
lazy[secondChildIndex].lr +%= 1 &+ lazy[secondChildIndex].ll
lazy[secondChildIndex].rr +%= lazy[secondChildIndex].rl
}
default:
break
}
}
// because the processing segment is fully in range - return
return
}
// not completely in range (but overlaps) process children
let mid = (range.lowerBound + range.upperBound) / 2
processRangeUpdate(nodeIndex: firstChildIndex, range: range.lowerBound...mid, queryRange: queryRange, processMode: processMode)
processRangeUpdate(nodeIndex: secondChildIndex, range: mid+1...range.upperBound, queryRange: queryRange, processMode: processMode)
//Update this node with the results of the children
tree[nodeIndex].merge(tree[firstChildIndex], tree[secondChildIndex])
}
private func updateRange(queryRange: ClosedRange<Int>, processMode: ProcessMode) {
processRangeUpdate(nodeIndex: 0, range: 0...length-1, queryRange: queryRange, processMode: processMode)
}
private func processSumInRange(range: ClosedRange<Int>, queryRange: ClosedRange<Int>, nodeIndex: Int, processMode: ProcessMode) -> Int64 {
let firstChildIndex = 2 * nodeIndex + 1
let secondChildIndex = 2 * nodeIndex + 2
if lazy[nodeIndex].isFilled {
//// update tree[si] according to RR,LR,RL,LL
let deltaLeft = lazy[nodeIndex].rl *% tree[nodeIndex].right &+ lazy[nodeIndex].ll *% tree[nodeIndex].left
let deltaRight = lazy[nodeIndex].rr *% tree[nodeIndex].right &+ lazy[nodeIndex].lr *% tree[nodeIndex].left
tree[nodeIndex].left +%= deltaLeft
tree[nodeIndex].right +%= deltaRight
// leaf node check
if range.count != 1 {
// propagate lazy data to child nodes
addToLazyNode(childIndex: firstChildIndex, parent: lazy[nodeIndex])
addToLazyNode(childIndex: secondChildIndex, parent: lazy[nodeIndex])
}
// lazy data is processed, set values to 0
lazy[nodeIndex].reset()
}
if range.isEmpty || !range.overlaps(queryRange) {
return 0
}
// this segment completely lies in range
if queryRange.containsFully(range) {
switch processMode {
case .left:
return tree[nodeIndex].left
case .right:
return tree[nodeIndex].right
default:
return 0
}
}
// part of this segment overlaps with query range
let mid = (range.lowerBound + range.upperBound) / 2;
return processSumInRange(range: range.lowerBound...mid, queryRange: queryRange, nodeIndex: firstChildIndex, processMode: processMode) +%
processSumInRange(range: mid+1...range.upperBound, queryRange: queryRange, nodeIndex: secondChildIndex, processMode: processMode)
}
private func addToLazyNode(childIndex: Int, parent: LazyNode) {
if !tree[childIndex].isProcessed {
return
}
lazy[childIndex].update(parent: parent)
}
// get sum of elements in range
func sumInRange(queryRange: ClosedRange<Int>, processMode: ProcessMode) -> Int64 {
return processSumInRange(range: 0...length-1, queryRange: queryRange, nodeIndex: 0, processMode: processMode)
}
func processElement(index: Int) -> Int64 {
let sumLeft = index > 0 ? sumInRange(queryRange: 0...index-1, processMode: .left) : 0
let sumRight = index < length - 1 ? sumInRange(queryRange: index+1...length-1, processMode: .right) : 0
let total = (1 &+ sumLeft +% sumRight)
// update current node/nest combinations
updateRange(queryRange: index...index, processMode: .initial)
if (index > 0) {
// update nodes on the left
updateRange(queryRange: 0...index-1, processMode: .right)
}
if (index < length - 1) {
// update nodes on the right
updateRange(queryRange: index+1...length-1, processMode: .left)
}
return total
}
}
public func solution(_ H: inout [Int]) -> Int {
var nests = H.enumerated().map { Nest(index: $0, height: $1) }
nests.sort { $0.height < $1.height }
let tree = LazySegmentTree(length: H.count)
var total: Int64 = 0
for nest in nests {
total +%= tree.processElement(index: nest.index)
}
return Int(total)
}
//
// Chromium.swift
// Codility
//
// Created by Werner Altewischer on 1/21/20.
//
import Foundation
private let MOD: Int64 = 1_000_000_007
// Define convenience operators to take the modulus into account for additions and multiplications
infix operator +% : AdditionPrecedence
infix operator *% : MultiplicationPrecedence
infix operator +%= : AssignmentPrecedence
extension Int64 {
fileprivate static func +%(lhs: Int64, rhs: Int64) -> Int64 {
return (lhs &+ rhs) % MOD
}
fileprivate static func *%(lhs: Int64, rhs: Int64) -> Int64 {
return (lhs &* rhs) % MOD
}
fileprivate static func +%= (lhs: inout Int64, rhs: Int64) {
lhs = (lhs &+ rhs) % MOD
}
}
extension ClosedRange {
func containsFully(_ other: ClosedRange) -> Bool {
return self.lowerBound <= other.lowerBound && self.upperBound >= other.upperBound
}
}
private struct Nest {
let index: Int
let height: Int
}
private final class Node {
init(left: Int64 = 0, right: Int64 = 0, total: Int64 = 0) {
self.left = left
self.right = right
}
var right: Int64
var left: Int64
var isProcessed: Bool {
return left != 0 || right != 0
}
func merge(_ first: Node, _ second: Node) {
self.left = first.left +% second.left
self.right = first.right +% second.right
}
}
private final class LazyNode {
var ll: Int64 = 0
var rl: Int64 = 0
var lr: Int64 = 0
var rr: Int64 = 0
var isFilled: Bool {
return ll != 0 || rr != 0 || lr != 0 || rl != 0
}
func reset() {
ll = 0
lr = 0
rl = 0
rr = 0
}
func update(parent: LazyNode) {
let deltaRR = parent.rr &+ parent.rr *% self.rr &+ parent.lr *% self.rl
let deltaLR = parent.lr &+ parent.lr *% self.ll &+ self.lr *% parent.rr
let deltaLL = parent.ll &+ parent.ll *% self.ll &+ parent.rl *% self.lr
let deltaRL = parent.rl &+ parent.rl *% self.rr &+ self.rl *% parent.ll
self.rr +%= deltaRR
self.lr +%= deltaLR
self.ll +%= deltaLL
self.rl +%= deltaRL
}
}
private enum ProcessMode {
case left, right, initial;
}
private final class LazySegmentTree {
private let tree: [Node]
private let lazy: [LazyNode]
private let length: Int
init(length: Int) {
var pow2 = 1
while pow2 < length {
pow2 <<= 1
}
//Maximum size of segment tree
let maxTreeSize = 2 * pow2 - 1;
var nodes = [Node]()
nodes.reserveCapacity(maxTreeSize)
var lazyNodes = [LazyNode]()
lazyNodes.reserveCapacity(maxTreeSize)
for _ in 0..<maxTreeSize {
nodes.append(Node())
lazyNodes.append(LazyNode())
}
self.length = length
self.tree = nodes
self.lazy = lazyNodes
}
private func processRangeUpdate(nodeIndex: Int, range: ClosedRange<Int>, queryRange: ClosedRange<Int>, processMode: ProcessMode) {
// out of range
if !range.overlaps(queryRange) {
return
}
let lazyNode = lazy[nodeIndex]
let treeNode = tree[nodeIndex]
let firstChildIndex = 2 * nodeIndex + 1
let secondChildIndex = 2 * nodeIndex + 2
if lazyNode.isFilled {
let deltaLeft = lazyNode.rl *% treeNode.right + lazyNode.ll *% treeNode.left
let deltaRight = lazyNode.rr *% treeNode.right + lazyNode.lr *% treeNode.left
treeNode.left +%= deltaLeft
treeNode.right +%= deltaRight
// leaf node check
if range.count != 1 {
// Propagate Values to lazy children...
addToLazyNode(childIndex: firstChildIndex, parent: lazyNode)
addToLazyNode(childIndex: secondChildIndex, parent: lazyNode)
}
// because the update is made, set lazy values from this node to 0
lazyNode.reset()
}
// Current segment is fully in range
if queryRange.containsFully(range) {
switch processMode {
case .left:
treeNode.left +%= treeNode.right
case .right:
treeNode.right +%= treeNode.left
case .initial:
treeNode.left = 1
treeNode.right = 1
}
// leaf node check
if range.count != 1 {
// When Update R: LR += 1 + LL; RR += RL
// When Update L: RL += 1 + RR; LL += LR;
switch processMode {
case .left:
if tree[firstChildIndex].isProcessed {
lazy[firstChildIndex].rl +%= 1 &+ lazy[firstChildIndex].rr
lazy[firstChildIndex].ll +%= lazy[firstChildIndex].lr
}
if tree[secondChildIndex].isProcessed {
lazy[secondChildIndex].rl +%= 1 &+ lazy[secondChildIndex].rr
lazy[secondChildIndex].ll +%= lazy[secondChildIndex].lr
}
case .right:
if tree[firstChildIndex].isProcessed {
lazy[firstChildIndex].lr +%= 1 &+ lazy[firstChildIndex].ll
lazy[firstChildIndex].rr +%= lazy[firstChildIndex].rl
}
if tree[secondChildIndex].isProcessed {
lazy[secondChildIndex].lr +%= 1 &+ lazy[secondChildIndex].ll
lazy[secondChildIndex].rr +%= lazy[secondChildIndex].rl
}
default:
break
}
}
// because the processing segment is fully in range - return
return
}
// not completely in range (but overlaps) process children
let mid = (range.lowerBound + range.upperBound) / 2
processRangeUpdate(nodeIndex: firstChildIndex, range: range.lowerBound...mid, queryRange: queryRange, processMode: processMode)
processRangeUpdate(nodeIndex: secondChildIndex, range: mid+1...range.upperBound, queryRange: queryRange, processMode: processMode)
//Update this node with the results of the children
tree[nodeIndex].merge(tree[firstChildIndex], tree[secondChildIndex])
}
private func updateRange(queryRange: ClosedRange<Int>, processMode: ProcessMode) {
processRangeUpdate(nodeIndex: 0, range: 0...length-1, queryRange: queryRange, processMode: processMode)
}
private func processSumInRange(range: ClosedRange<Int>, queryRange: ClosedRange<Int>, nodeIndex: Int, processMode: ProcessMode) -> Int64 {
let firstChildIndex = 2 * nodeIndex + 1
let secondChildIndex = 2 * nodeIndex + 2
if lazy[nodeIndex].isFilled {
//// update tree[si] according to RR,LR,RL,LL
let deltaLeft = lazy[nodeIndex].rl *% tree[nodeIndex].right &+ lazy[nodeIndex].ll *% tree[nodeIndex].left
let deltaRight = lazy[nodeIndex].rr *% tree[nodeIndex].right &+ lazy[nodeIndex].lr *% tree[nodeIndex].left
tree[nodeIndex].left +%= deltaLeft
tree[nodeIndex].right +%= deltaRight
// leaf node check
if range.count != 1 {
// propagate lazy data to child nodes
addToLazyNode(childIndex: firstChildIndex, parent: lazy[nodeIndex])
addToLazyNode(childIndex: secondChildIndex, parent: lazy[nodeIndex])
}
// lazy data is processed, set values to 0
lazy[nodeIndex].reset()
}
if range.isEmpty || !range.overlaps(queryRange) {
return 0
}
// this segment completely lies in range
if queryRange.containsFully(range) {
switch processMode {
case .left:
return tree[nodeIndex].left
case .right:
return tree[nodeIndex].right
default:
return 0
}
}
// part of this segment overlaps with query range
let mid = (range.lowerBound + range.upperBound) / 2;
return processSumInRange(range: range.lowerBound...mid, queryRange: queryRange, nodeIndex: firstChildIndex, processMode: processMode) +%
processSumInRange(range: mid+1...range.upperBound, queryRange: queryRange, nodeIndex: secondChildIndex, processMode: processMode)
}
private func addToLazyNode(childIndex: Int, parent: LazyNode) {
if !tree[childIndex].isProcessed {
return
}
lazy[childIndex].update(parent: parent)
}
// get sum of elements in range
func sumInRange(queryRange: ClosedRange<Int>, processMode: ProcessMode) -> Int64 {
return processSumInRange(range: 0...length-1, queryRange: queryRange, nodeIndex: 0, processMode: processMode)
}
func processElement(index: Int) -> Int64 {
let sumLeft = index > 0 ? sumInRange(queryRange: 0...index-1, processMode: .left) : 0
let sumRight = index < length - 1 ? sumInRange(queryRange: index+1...length-1, processMode: .right) : 0
let total = (1 &+ sumLeft +% sumRight)
// update current node/nest combinations
updateRange(queryRange: index...index, processMode: .initial)
if (index > 0) {
// update nodes on the left
updateRange(queryRange: 0...index-1, processMode: .right)
}
if (index < length - 1) {
// update nodes on the right
updateRange(queryRange: index+1...length-1, processMode: .left)
}
return total
}
}
public func solution(_ H: inout [Int]) -> Int {
var nests = H.enumerated().map { Nest(index: $0, height: $1) }
nests.sort { $0.height < $1.height }
let tree = LazySegmentTree(length: H.count)
var total: Int64 = 0
for nest in nests {
total +%= tree.processElement(index: nest.index)
}
return Int(total)
}
The solution obtained perfect score.
Small random tests, failing exponential-time solutions and depending on the size of values. Requires int64
Small random tests, failing exponential-time solutions and depending on the size of values.