From 81df9ce39e714993b55c5a82747b6fe5032a9e6d Mon Sep 17 00:00:00 2001 From: naminodarie Date: Sat, 12 Sep 2020 03:46:22 +0900 Subject: [PATCH 1/4] add CeilPow2 --- AtCoderLibrary/Internal/Math/CeilPow2.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 AtCoderLibrary/Internal/Math/CeilPow2.cs diff --git a/AtCoderLibrary/Internal/Math/CeilPow2.cs b/AtCoderLibrary/Internal/Math/CeilPow2.cs new file mode 100644 index 00000000..a248dc51 --- /dev/null +++ b/AtCoderLibrary/Internal/Math/CeilPow2.cs @@ -0,0 +1,20 @@ +using System.Numerics; + +namespace AtCoder.Internal +{ + public static partial class InternalMath + { + /// + /// ≤ 2**x を満たす最小のx + /// + /// + /// 制約: 0≤ + /// + public static int CeilPow2(int n) + { + var un = (uint)n; + if (un <= 1) return 0; + return BitOperations.Log2(un - 1) + 1; + } + } +} From eaf704cf52025e1442ebdeb83244a23ee2a9f8c6 Mon Sep 17 00:00:00 2001 From: naminodarie Date: Sat, 12 Sep 2020 03:46:31 +0900 Subject: [PATCH 2/4] add Segtree --- AtCoderLibrary/Segtree.cs | 263 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 AtCoderLibrary/Segtree.cs diff --git a/AtCoderLibrary/Segtree.cs b/AtCoderLibrary/Segtree.cs new file mode 100644 index 00000000..767fb103 --- /dev/null +++ b/AtCoderLibrary/Segtree.cs @@ -0,0 +1,263 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using AtCoder.Internal; + +namespace AtCoder +{ + /// + /// モノイドを定義するインターフェイスです。 + /// + /// 操作を行う型。 + public interface IMonoidOperator + { + /// + /// Operate(x, ) = xを満たす単位元。 + /// + T Identity { get; } + /// + /// 結合律 Operate(Operate(a, b), c) = Operate(a, Operate(b, c)) を満たす写像。 + /// + T Operate(T x, T y); + } + + /// + /// 長さ N の配列に対し、 + /// + /// + /// 要素の 1 点変更 + /// + /// + /// 区間の要素の総積の取得 + /// + /// + /// を O(log N) で求めることが出来るデータ構造です。 + /// + public class Segtree where TOp : struct, IMonoidOperator + { + private static readonly TOp op = default; + + /// + /// 数列 a の長さ n を返します。 + /// + public int Length { get; } + + private readonly int log; + private readonly int size; + private readonly TValue[] d; + + + /// + /// 長さ の数列 a を持つ クラスの新しいインスタンスを作ります。初期値は です。 + /// + /// + /// 制約: 0≤≤10^8 + /// 計算量: O() + /// + /// 配列の長さ + public Segtree(int n) + { + Debug.Assert(op.Operate(op.Identity, op.Identity).Equals(op.Identity), "op.Operate(op.Identity, op.Identity) != op.Identity"); + Length = n; + log = InternalMath.CeilPow2(n); + size = 1 << log; + d = new TValue[2 * size]; + Array.Fill(d, op.Identity); + } + + /// + /// 長さ n=.Length の数列 a を持つ クラスの新しいインスタンスを作ります。初期値は です。 + /// + /// + /// 制約: 0≤≤10^8 + /// 計算量: O() + /// + /// 配列の長さ + public Segtree(TValue[] v) : this(v.Length) + { + for (int i = 0; i < v.Length; i++) d[size + i] = v[i]; + for (int i = size - 1; i >= 1; i--) + { + Update(i); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Update(int k) => d[k] = op.Operate(d[2 * k], d[2 * k + 1]); + + /// + /// a[] を返します。 + /// + /// + /// 制約: 0≤<n + /// 計算量(set): O(log n) + /// 計算量(get): O(1) + /// + /// + public TValue this[int p] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + Debug.Assert((uint)p < Length); + p += size; + d[p] = value; + for (int i = 1; i <= log; i++) Update(p >> i); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + Debug.Assert((uint)p < Length); + return d[p + size]; + } + } + + /// + /// (a[], ..., a[ - 1]) を返します。 = のときは  を返します。 + /// + /// + /// 制約: 0≤≤n + /// 計算量: O(log n) + /// + /// (a[], ..., a[ - 1]) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TValue Prod(int l, int r) + { + Debug.Assert(0 <= l && l <= r && r <= Length); + TValue sml = op.Identity, smr = op.Identity; + l += size; + r += size; + + while (l < r) + { + if ((l & 1) != 0) sml = op.Operate(sml, d[l++]); + if ((r & 1) != 0) smr = op.Operate(d[--r], smr); + l >>= 1; + r >>= 1; + } + return op.Operate(sml, smr); + } + + /// + /// (a[0], ..., a[n - 1]) を返します。n = 0 のときは  を返します。 + /// + /// + /// 計算量: O(1) + /// + /// (a[0], ..., a[n - 1]) + public TValue AllProd => d[1]; + + /// + /// 以下の条件を両方満たす r を(いずれか一つ)返します。 + /// + /// + /// r = もしくは (op(a[], a[ + 1], ..., a[r - 1])) = true + /// + /// + /// r = n もしくは (op(a[], a[ + 1], ..., a[r])) = false + /// + /// + /// が単調だとすれば、(op(a[], a[ + 1], ..., a[r - 1])) = true となる最大の r、と解釈することが可能です。 + /// + /// + /// 制約 + /// + /// + /// を同じ引数で呼んだ時、返り値は等しい(=副作用はない)。 + /// + /// + /// () = true + /// + /// + /// 0≤≤n + /// + /// + /// 計算量: O(log n) + /// + public int MaxRight(int l, Predicate f) + { + Debug.Assert((uint)l <= Length); + Debug.Assert(f(op.Identity)); + if (l == Length) return Length; + l += size; + var sm = op.Identity; + do + { + while (l % 2 == 0) l >>= 1; + if (!f(op.Operate(sm, d[l]))) + { + while (l < size) + { + l = (2 * l); + if (f(op.Operate(sm, d[l]))) + { + sm = op.Operate(sm, d[l]); + l++; + } + } + return l - size; + } + sm = op.Operate(sm, d[l]); + l++; + } while ((l & -l) != l); + return Length; + } + + /// + /// 以下の条件を両方満たす r を(いずれか一つ)返します。 + /// + /// + /// l = もしくは (op(a[l], a[l + 1], ..., a[ - 1])) = true + /// + /// + /// l = 0 もしくは (op(a[l - 1], a[l], ..., a[ - 1])) = false + /// + /// + /// が単調だとすれば、(op(a[l], a[l + 1], ..., a[ - 1])) = true となる最小の l、と解釈することが可能です。 + /// + /// + /// 制約 + /// + /// + /// を同じ引数で呼んだ時、返り値は等しい(=副作用はない)。 + /// + /// + /// () = true + /// + /// + /// 0≤≤n + /// + /// + /// 計算量: O(log n) + /// + public int MinLeft(int r, Predicate f) + { + Debug.Assert((uint)r <= Length); + Debug.Assert(f(op.Identity)); + if (r == 0) return 0; + r += size; + var sm = op.Identity; + do + { + r--; + while (r > 1 && (r % 2) != 0) r >>= 1; + if (!f(op.Operate(d[r], sm))) + { + while (r < size) + { + r = (2 * r + 1); + if (f(op.Operate(d[r], sm))) + { + sm = op.Operate(d[r], sm); + r--; + } + } + return r + 1 - size; + } + sm = op.Operate(d[r], sm); + } while ((r & -r) != r); + return 0; + } + } +} From 6b94abd3b464b7122fc6d2a420f204bbe35c050c Mon Sep 17 00:00:00 2001 From: naminodarie Date: Sat, 12 Sep 2020 12:57:29 +0900 Subject: [PATCH 3/4] Add SegTree.DebugView --- AtCoderLibrary/Segtree.cs | 47 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/AtCoderLibrary/Segtree.cs b/AtCoderLibrary/Segtree.cs index 767fb103..84f46bce 100644 --- a/AtCoderLibrary/Segtree.cs +++ b/AtCoderLibrary/Segtree.cs @@ -34,6 +34,7 @@ public interface IMonoidOperator /// /// を O(log N) で求めることが出来るデータ構造です。 /// + [DebuggerTypeProxy(typeof(Segtree<,>.DebugView))] public class Segtree where TOp : struct, IMonoidOperator { private static readonly TOp op = default; @@ -259,5 +260,51 @@ public int MinLeft(int r, Predicate f) } while ((r & -r) != r); return 0; } + + + [DebuggerDisplay("{" + nameof(value) + "}", Name = "{" + nameof(key) + ",nq}")] + private struct DebugItem + { + public DebugItem(int l, int r, TValue value) + { + if (r - l == 1) + key = $"[{l}]"; + else + key = $"[{l}-{r})"; + this.value = value; + } + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private readonly string key; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private readonly TValue value; + } + private class DebugView + { + private readonly Segtree segtree; + public DebugView(Segtree segtree) + { + this.segtree = segtree; + } + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public DebugItem[] Items + { + get + { + var items = new List(segtree.Length); + for (int len = segtree.size; len > 0; len >>= 1) + { + int unit = segtree.size / len; + for (int i = 0; i < len; i++) + { + int l = i * unit; + int r = System.Math.Min(l + unit, segtree.Length); + if (l < segtree.Length) + items.Add(new DebugItem(l, r, segtree.d[i + len])); + } + } + return items.ToArray(); + } + } + } } } From b828e41fa3a718643ae96f92fc3dae032be5717b Mon Sep 17 00:00:00 2001 From: naminodarie Date: Sun, 13 Sep 2020 02:16:05 +0900 Subject: [PATCH 4/4] AssertMonoid --- AtCoderLibrary/Segtree.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/AtCoderLibrary/Segtree.cs b/AtCoderLibrary/Segtree.cs index 84f46bce..a7907436 100644 --- a/AtCoderLibrary/Segtree.cs +++ b/AtCoderLibrary/Segtree.cs @@ -59,7 +59,7 @@ public class Segtree where TOp : struct, IMonoidOperator /// 配列の長さ public Segtree(int n) { - Debug.Assert(op.Operate(op.Identity, op.Identity).Equals(op.Identity), "op.Operate(op.Identity, op.Identity) != op.Identity"); + AssertMonoid(op.Identity); Length = n; log = InternalMath.CeilPow2(n); size = 1 << log; @@ -101,6 +101,7 @@ public TValue this[int p] [MethodImpl(MethodImplOptions.AggressiveInlining)] set { + AssertMonoid(value); Debug.Assert((uint)p < Length); p += size; d[p] = value; @@ -110,6 +111,7 @@ public TValue this[int p] get { Debug.Assert((uint)p < Length); + AssertMonoid(d[p + size]); return d[p + size]; } } @@ -137,6 +139,7 @@ public TValue Prod(int l, int r) l >>= 1; r >>= 1; } + AssertMonoid(op.Operate(sml, smr)); return op.Operate(sml, smr); } @@ -306,5 +309,18 @@ public DebugItem[] Items } } } + + /// + /// Debug ビルドにおいて、Monoid が正しいかチェックする。 + /// + /// + [Conditional("DEBUG")] + public static void AssertMonoid(TValue value) + { + Debug.Assert(op.Operate(value, op.Identity).Equals(value), + $"{nameof(op.Operate)}({value}, {op.Identity}) != {value}"); + Debug.Assert(op.Operate(op.Identity, value).Equals(value), + $"{nameof(op.Operate)}({op.Identity}, {value}) != {value}"); + } } }