Repository navigation
Expand file tree
/
Copy pathArrayAndSimpleQueries.cs
More file actions
111 lines (94 loc) · 3.3 KB
/
Copy pathArrayAndSimpleQueries.cs
File metadata and controls
111 lines (94 loc) · 3.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
namespace HackerRank.Solutions.DataStructures;
/// <summary>
/// Array and simple queries — "1 i j" moves a[i..j] to the front and "2 i j" moves it to the
/// back; return |a[1] - a[n]| and the final array. An implicit treap (keyed by position)
/// cuts out and re-attaches any range with two splits and two merges, O(log n) expected per
/// query.
/// https://www.hackerrank.com/challenges/array-and-simple-queries
/// </summary>
public static class ArrayAndSimpleQueries
{
private sealed class Treap(int value, int priority)
{
public int Value { get; } = value;
public int Priority { get; } = priority;
public int Size { get; set; } = 1;
public Treap? Left { get; set; }
public Treap? Right { get; set; }
}
/// <param name="a">The array.</param>
/// <param name="queries">Each query as <c>[type, i, j]</c>, 1-based and inclusive.</param>
public static (long Difference, int[] Final) Solve(IReadOnlyList<int> a, IReadOnlyList<IReadOnlyList<int>> queries)
{
var random = new Random(12345);
Treap? root = null;
foreach (var value in a)
{
root = Merge(root, new Treap(value, random.Next()));
}
foreach (var query in queries)
{
var (before, rest) = Split(root, query[1] - 1);
var (middle, after) = Split(rest, query[2] - query[1] + 1);
var others = Merge(before, after);
root = query[0] == 1 ? Merge(middle, others) : Merge(others, middle);
}
var final = new List<int>(a.Count);
Collect(root, final);
return (Math.Abs((long)final[0] - final[^1]), final.ToArray());
}
private static int SizeOf(Treap? node) => node?.Size ?? 0;
private static void Update(Treap node) => node.Size = 1 + SizeOf(node.Left) + SizeOf(node.Right);
/// <summary>Splits into the first <paramref name="count"/> elements and the rest.</summary>
private static (Treap? Left, Treap? Right) Split(Treap? node, int count)
{
if (node is null)
{
return (null, null);
}
if (SizeOf(node.Left) >= count)
{
var (left, right) = Split(node.Left, count);
node.Left = right;
Update(node);
return (left, node);
}
else
{
var (left, right) = Split(node.Right, count - SizeOf(node.Left) - 1);
node.Right = left;
Update(node);
return (node, right);
}
}
private static Treap? Merge(Treap? left, Treap? right)
{
if (left is null || right is null)
{
return left ?? right;
}
if (left.Priority > right.Priority)
{
left.Right = Merge(left.Right, right);
Update(left);
return left;
}
right.Left = Merge(left, right.Left);
Update(right);
return right;
}
private static void Collect(Treap? node, List<int> output)
{
var stack = new Stack<Treap>();
while (node is not null || stack.Count > 0)
{
for (; node is not null; node = node.Left)
{
stack.Push(node);
}
node = stack.Pop();
output.Add(node.Value);
node = node.Right;
}
}
}