|
| 1 | +// Package sqrt contains algorithms and data structures that contains a √n in their complexity |
| 2 | +package sqrt |
| 3 | + |
| 4 | +import "math" |
| 5 | + |
| 6 | +// Sqrt (or Square Root) Decomposition is a technique used for query an array and perform updates |
| 7 | +// Inside this package is described its most simple data structure, you can find more at: https://cp-algorithms.com/data_structures/sqrt_decomposition.html |
| 8 | +// |
| 9 | +// Formally, You can use SqrtDecomposition only if: |
| 10 | +// |
| 11 | +// Given a function $Query:E_1,...,E_n\rightarrow Q$ |
| 12 | +// |
| 13 | +// if $\exist unionQ:Q,Q\rightarrow Q$ |
| 14 | +// |
| 15 | +// s.t. |
| 16 | +// |
| 17 | +// - $\forall n\in \N > 1, 1\le i<n, E_1,..., E_n\in E \\ query(E_1,..., E_n)=unionQ(query(E_1,..., E_i), query(E_{i+1},...,E_n))$ |
| 18 | +// |
| 19 | +// - (Only if you want use $update$ function) |
| 20 | +// $\forall n\in \N > 0, E_1,..., E_n\in E \\ query(E_1,...,E_{new},..., E_n)=updateQ(query(E_1,...,E_{old},...,E_n), indexof(E_{old}), E_{new})$ |
| 21 | +type SqrtDecomposition[E any, Q any] struct { |
| 22 | + querySingleElement func(element E) Q |
| 23 | + unionQ func(q1 Q, q2 Q) Q |
| 24 | + updateQ func(oldQ Q, oldE E, newE E) (newQ Q) |
| 25 | + |
| 26 | + elements []E |
| 27 | + blocks []Q |
| 28 | + blockSize uint64 |
| 29 | +} |
| 30 | + |
| 31 | +// Create a new SqrtDecomposition instance with the parameters as specified by SqrtDecomposition comment |
| 32 | +// Assumptions: |
| 33 | +// - len(elements) > 0 |
| 34 | +func NewSqrtDecomposition[E any, Q any]( |
| 35 | + elements []E, |
| 36 | + querySingleElement func(element E) Q, |
| 37 | + unionQ func(q1 Q, q2 Q) Q, |
| 38 | + updateQ func(oldQ Q, oldE E, newE E) (newQ Q), |
| 39 | +) *SqrtDecomposition[E, Q] { |
| 40 | + sqrtDec := &SqrtDecomposition[E, Q]{ |
| 41 | + querySingleElement: querySingleElement, |
| 42 | + unionQ: unionQ, |
| 43 | + updateQ: updateQ, |
| 44 | + elements: elements, |
| 45 | + } |
| 46 | + sqrt := math.Sqrt(float64(len(sqrtDec.elements))) |
| 47 | + blockSize := uint64(sqrt) |
| 48 | + numBlocks := uint64(math.Ceil(float64(len(elements)) / float64(blockSize))) |
| 49 | + sqrtDec.blocks = make([]Q, numBlocks) |
| 50 | + for i := uint64(0); i < uint64(len(elements)); i++ { |
| 51 | + if i%blockSize == 0 { |
| 52 | + sqrtDec.blocks[i/blockSize] = sqrtDec.querySingleElement(elements[i]) |
| 53 | + } else { |
| 54 | + sqrtDec.blocks[i/blockSize] = sqrtDec.unionQ(sqrtDec.blocks[i/blockSize], sqrtDec.querySingleElement(elements[i])) |
| 55 | + } |
| 56 | + } |
| 57 | + sqrtDec.blockSize = blockSize |
| 58 | + return sqrtDec |
| 59 | +} |
| 60 | + |
| 61 | +// Performs a query from index start to index end (non included) |
| 62 | +// Assumptions: |
| 63 | +// - start < end |
| 64 | +// - start and end are valid |
| 65 | +func (s *SqrtDecomposition[E, Q]) Query(start uint64, end uint64) Q { |
| 66 | + firstIndexNextBlock := ((start / s.blockSize) + 1) * s.blockSize |
| 67 | + q := s.querySingleElement(s.elements[start]) |
| 68 | + if firstIndexNextBlock > end { // if in same block |
| 69 | + start++ |
| 70 | + for start < end { |
| 71 | + q = s.unionQ(q, s.querySingleElement(s.elements[start])) |
| 72 | + start++ |
| 73 | + } |
| 74 | + } else { |
| 75 | + // left side |
| 76 | + start++ |
| 77 | + for start < firstIndexNextBlock { |
| 78 | + q = s.unionQ(q, s.querySingleElement(s.elements[start])) |
| 79 | + start++ |
| 80 | + } |
| 81 | + |
| 82 | + //middle part |
| 83 | + endBlock := end / s.blockSize |
| 84 | + for i := firstIndexNextBlock / s.blockSize; i < endBlock; i++ { |
| 85 | + q = s.unionQ(q, s.blocks[i]) |
| 86 | + } |
| 87 | + |
| 88 | + // right part |
| 89 | + for i := endBlock * s.blockSize; i < end; i++ { |
| 90 | + q = s.unionQ(q, s.querySingleElement(s.elements[i])) |
| 91 | + } |
| 92 | + } |
| 93 | + return q |
| 94 | +} |
| 95 | + |
| 96 | +// Assumptions: |
| 97 | +// - index is valid |
| 98 | +func (s *SqrtDecomposition[E, Q]) Update(index uint64, newElement E) { |
| 99 | + i := index / s.blockSize |
| 100 | + s.blocks[i] = s.updateQ(s.blocks[i], s.elements[index], newElement) |
| 101 | + s.elements[index] = newElement |
| 102 | +} |
0 commit comments