-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathNOTATRI.cs
150 lines (125 loc) · 5.14 KB
/
NOTATRI.cs
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
using System;
using System.Collections.Generic;
// https://www.spoj.com/problems/NOTATRI/ #binary-search #sliding-window #sorting
// Figures out how many combinations of 3 sticks can't form a triangle.
public static class NOTATRI
{
// Imagine how we would brute force this. Three for loops, i=0, j=i+1, k=j+1. This considers
// every combination (set of three sticks, order not important) exactly once. If we sort the
// sticks by length first, then once we've picked the short and medium-length sticks, we can
// do a predicate-based binary search for the first stick longer than their sum. Every stick
// after that stick is also longer, so they contribute to the non-triangle count as well.
// This gets us to O(n^2 * logn). But we actually only need to binary search once for a given
// short stick and the initial medium stick, to get an initial long stick. Then while the
// short stick stays the same, we have a sliding window on the medium and long sticks. Bump
// the medium stick, and then proceed from the previous long stick index until the new long
// stick is long enough again. This is O(n^2).
public static int Solve(int stickCount, int[] stickLengths)
{
Array.Sort(stickLengths);
int nonTriangleCombinations = 0;
for (int smallStickIndex = 0;
smallStickIndex < stickCount - 2;
++smallStickIndex)
{
int smallStickLength = stickLengths[smallStickIndex];
int initialMediumStickIndex = smallStickIndex + 1;
int initialMediumStickLength = stickLengths[initialMediumStickIndex];
int? initialLongStickIndex = BinarySearch.Search(
start: initialMediumStickIndex + 1,
end: stickCount - 1,
verifier: i => stickLengths[i] > smallStickLength + initialMediumStickLength,
mode: BinarySearch.Mode.FalseToTrue);
if (!initialLongStickIndex.HasValue)
return nonTriangleCombinations;
int mediumStickIndex = initialMediumStickIndex;
int mediumStickLength = initialMediumStickLength;
int longStickIndex = initialLongStickIndex.Value;
int longStickLength = stickLengths[longStickIndex];
while (longStickIndex < stickCount)
{
nonTriangleCombinations += stickCount - longStickIndex;
++mediumStickIndex;
mediumStickLength = stickLengths[mediumStickIndex];
while (longStickLength <= smallStickLength + mediumStickLength
&& longStickIndex < stickCount)
{
++longStickIndex;
if (longStickIndex < stickCount)
{
longStickLength = stickLengths[longStickIndex];
}
}
}
}
return nonTriangleCombinations;
}
}
// This facilitates predicate-based binary searching, where the values being searched on
// satisfy the predicate in an ordered manner, in one of two ways:
// [false false false ... false true ... true true true] (true => anything larger is true)
// [true true true ... true false ... false false false] (true => anything smaller is true)
// In the first, the goal of the search is to locate the smallest value satisfying the predicate.
// In the second, the goal of the search is to locate the largest value satisfying the predicate.
// For more info, see: https://www.topcoder.com/community/data-science/data-science-tutorials/binary-search/.
public static class BinarySearch
{
public enum Mode
{
FalseToTrue,
TrueToFalse
};
public static int? Search(int start, int end, Predicate<int> verifier, Mode mode)
=> mode == Mode.FalseToTrue
? SearchFalseToTrue(start, end, verifier)
: SearchTrueToFalse(start, end, verifier);
private static int? SearchFalseToTrue(int start, int end, Predicate<int> verifier)
{
if (start > end) return null;
int mid;
while (start != end)
{
mid = start + (end - start) / 2;
if (verifier(mid))
{
end = mid;
}
else
{
start = mid + 1;
}
}
return verifier(start) ? start : (int?)null;
}
private static int? SearchTrueToFalse(int start, int end, Predicate<int> verifier)
{
if (start > end) return null;
int mid;
while (start != end)
{
mid = start + (end - start + 1) / 2;
if (verifier(mid))
{
start = mid;
}
else
{
end = mid - 1;
}
}
return verifier(start) ? start : (int?)null;
}
}
public static class Program
{
private static void Main()
{
int stickCount;
while ((stickCount = int.Parse(Console.ReadLine())) != 0)
{
int[] stickLengths = Array.ConvertAll(Console.ReadLine().Split(), int.Parse);
Console.WriteLine(
NOTATRI.Solve(stickCount, stickLengths));
}
}
}