2198. Number of Single Divisor Triplets 🔒
Problem Description
You are given a 0-indexed array of positive integers nums
.
A triplet of three distinct indices (i, j, k)
is called a single divisor triplet if the sum nums[i] + nums[j] + nums[k]
is divisible by exactly one of the three values: nums[i]
, nums[j]
, or nums[k]
.
Your task is to return the total number of single divisor triplets in the array nums
.
For example, if you have indices (i, j, k)
where:
nums[i] = 4
nums[j] = 6
nums[k] = 7
- Sum =
4 + 6 + 7 = 17
You would check if 17 is divisible by 4 (No), by 6 (No), and by 7 (No). Since none of them divide 17, this wouldn't be a single divisor triplet. You need exactly one of the three numbers to divide the sum.
The indices must be distinct, meaning i ≠ j
, j ≠ k
, and i ≠ k
.
Intuition
The key observation is that the values in the array are limited to the range [1, 100]
. This small range allows us to think about the problem differently - instead of iterating through all possible index triplets (which would be O(n³)), we can focus on the unique values and their frequencies.
Since we only care about the values at the three indices (not the indices themselves), we can count how many times each value appears in the array. Then, we can enumerate all possible combinations of three values from the range [1, 100]
.
For each combination of three values (a, b, c)
, we check if their sum a + b + c
is divisible by exactly one of them. If this condition is met, we need to count how many ways we can pick indices from the array to form this value combination.
The counting becomes interesting when some values are repeated:
- If all three values are different, we can freely choose any index with value
a
, any index with valueb
, and any index with valuec
. This gives uscount(a) × count(b) × count(c)
ways. - If two values are the same (say
a = b
), we need to pick two different indices with valuea
and one index with valuec
. The number of ways to pick two different indices fromcount(a)
occurrences iscount(a) × (count(a) - 1)
, giving uscount(a) × (count(a) - 1) × count(c)
total ways.
This approach reduces the time complexity significantly because we're only iterating through at most 100 × 100 × 100 = 1,000,000
combinations of values, regardless of the size of the input array.
Learn more about Math patterns.
Solution Approach
We implement the solution using counting and enumeration:
-
Count the frequency of each value: First, we use a
Counter
to count how many times each unique value appears in the arraynums
. This gives us a dictionary where keys are the values and values are their frequencies. -
Enumerate all possible value combinations: We iterate through all possible combinations of three values
(a, b, c)
from the counter. Since we're using the counter's keys, we only consider values that actually exist in the array. -
Check the single divisor condition: For each combination, we calculate the sum
s = a + b + c
and count how many of the three values divide the sum evenly. We use the expressionsum(s % v == 0 for v in (a, b, c))
to count divisors. If exactly one value divides the sum, we proceed to count the triplets. -
Count valid triplets based on value equality:
-
When
a == b
: We have two indices with the same valuea
and one with valuec
. The number of ways to choose 2 different indices fromx
occurrences ofa
isx × (x - 1)
(ordered pairs), multiplied byz
ways to choosec
, giving usx × (x - 1) × z
triplets. -
When
a == c
: Similar logic applies, giving usx × (x - 1) × y
triplets. -
When
b == c
: We getx × y × (y - 1)
triplets. -
When all values are different: We can freely choose one index for each value, giving us
x × y × z
triplets.
-
-
Accumulate the result: We add the count of valid triplets for each combination to our answer
ans
.
The algorithm handles the distinct indices requirement automatically through the counting logic - when two values are the same, we use count × (count - 1)
to ensure we pick different indices for those values.
This approach has a time complexity of O(m³)
where m
is the number of unique values in the array (at most 100), which is much more efficient than checking all O(n³)
index triplets directly.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example with nums = [4, 6, 7, 3, 2]
.
Step 1: Count frequencies
- Value 4 appears 1 time
- Value 6 appears 1 time
- Value 7 appears 1 time
- Value 3 appears 1 time
- Value 2 appears 1 time
Step 2: Enumerate value combinations
Let's check a few combinations:
Combination (4, 6, 7):
- Sum = 4 + 6 + 7 = 17
- Check divisibility: 17 % 4 = 1 (not divisible), 17 % 6 = 5 (not divisible), 17 % 7 = 3 (not divisible)
- Number of divisors = 0 (not exactly 1, so skip)
Combination (4, 6, 2):
- Sum = 4 + 6 + 2 = 12
- Check divisibility: 12 % 4 = 0 (divisible), 12 % 6 = 0 (divisible), 12 % 2 = 0 (divisible)
- Number of divisors = 3 (not exactly 1, so skip)
Combination (4, 7, 3):
- Sum = 4 + 7 + 3 = 14
- Check divisibility: 14 % 4 = 2 (not divisible), 14 % 7 = 0 (divisible), 14 % 3 = 2 (not divisible)
- Number of divisors = 1 (exactly 1! This is valid)
- All three values are different: count = 1 × 1 × 1 = 1 triplet
Combination (6, 7, 2):
- Sum = 6 + 7 + 2 = 15
- Check divisibility: 15 % 6 = 3 (not divisible), 15 % 7 = 1 (not divisible), 15 % 2 = 1 (not divisible)
- Number of divisors = 0 (not exactly 1, so skip)
Combination (3, 3, 6): (if we had duplicate 3s)
- If nums had two 3s, say
nums = [3, 3, 6, 7, 2]
- Sum = 3 + 3 + 6 = 12
- Check divisibility: 12 % 3 = 0 (divisible), 12 % 6 = 0 (divisible)
- Number of divisors = 2 (not exactly 1, so skip)
In this example, we found 1 valid single divisor triplet formed by indices containing values (4, 7, 3). The actual indices would be (0, 2, 3) since nums[0]=4, nums[2]=7, nums[3]=3.
The key insight is that instead of checking all C(5,3) = 10 possible index triplets, we only checked the unique value combinations, making the algorithm much more efficient for arrays with repeated values or when the value range is small.
Solution Implementation
1from typing import List
2from collections import Counter
3
4class Solution:
5 def singleDivisorTriplet(self, nums: List[int]) -> int:
6 # Count frequency of each number in the input array
7 frequency_map = Counter(nums)
8
9 # Initialize result counter for valid triplets
10 result = 0
11
12 # Iterate through all possible combinations of three values
13 for value_a, count_a in frequency_map.items():
14 for value_b, count_b in frequency_map.items():
15 for value_c, count_c in frequency_map.items():
16 # Calculate sum of the three values
17 triplet_sum = value_a + value_b + value_c
18
19 # Check how many values divide the sum evenly
20 divisor_count = sum(
21 triplet_sum % value == 0
22 for value in (value_a, value_b, value_c)
23 )
24
25 # Only proceed if exactly one value divides the sum
26 if divisor_count == 1:
27 # Calculate number of ways to form triplets based on duplicate values
28 if value_a == value_b:
29 # First two values are same: choose 2 from count_a, 1 from count_c
30 result += count_a * (count_a - 1) * count_c
31 elif value_a == value_c:
32 # First and third values are same: choose 2 from count_a, 1 from count_b
33 result += count_a * (count_a - 1) * count_b
34 elif value_b == value_c:
35 # Second and third values are same: choose 1 from count_a, 2 from count_b
36 result += count_a * count_b * (count_b - 1)
37 else:
38 # All three values are different: multiply their counts
39 result += count_a * count_b * count_c
40
41 return result
42
1class Solution {
2 public long singleDivisorTriplet(int[] nums) {
3 // Count frequency of each number (numbers are bounded by 1-100)
4 int[] frequency = new int[101];
5 for (int num : nums) {
6 frequency[num]++;
7 }
8
9 long totalTriplets = 0;
10
11 // Try all possible triplet values (a, b, c) from 1 to 100
12 for (int firstValue = 1; firstValue <= 100; firstValue++) {
13 for (int secondValue = 1; secondValue <= 100; secondValue++) {
14 for (int thirdValue = 1; thirdValue <= 100; thirdValue++) {
15 // Calculate sum of the triplet
16 int tripletSum = firstValue + secondValue + thirdValue;
17
18 // Get frequencies of each value
19 int firstFreq = frequency[firstValue];
20 int secondFreq = frequency[secondValue];
21 int thirdFreq = frequency[thirdValue];
22
23 // Count how many values in the triplet divide the sum
24 int divisorCount = 0;
25 if (tripletSum % firstValue == 0) {
26 divisorCount++;
27 }
28 if (tripletSum % secondValue == 0) {
29 divisorCount++;
30 }
31 if (tripletSum % thirdValue == 0) {
32 divisorCount++;
33 }
34
35 // Only process if exactly one value divides the sum
36 if (divisorCount == 1) {
37 // Calculate number of ways to form this triplet based on duplicate values
38 if (firstValue == secondValue) {
39 // Two values are the same (a == b), need to pick 2 from firstFreq
40 totalTriplets += (long) firstFreq * (firstFreq - 1) * thirdFreq;
41 } else if (firstValue == thirdValue) {
42 // Two values are the same (a == c), need to pick 2 from firstFreq
43 totalTriplets += (long) firstFreq * (firstFreq - 1) * secondFreq;
44 } else if (secondValue == thirdValue) {
45 // Two values are the same (b == c), need to pick 2 from secondFreq
46 totalTriplets += (long) firstFreq * secondFreq * (secondFreq - 1);
47 } else {
48 // All three values are distinct
49 totalTriplets += (long) firstFreq * secondFreq * thirdFreq;
50 }
51 }
52 }
53 }
54 }
55
56 return totalTriplets;
57 }
58}
59
1class Solution {
2public:
3 long long singleDivisorTriplet(vector<int>& nums) {
4 // Count frequency of each number (numbers are in range [1, 100])
5 int frequency[101]{};
6 for (int num : nums) {
7 ++frequency[num];
8 }
9
10 long long totalTriplets = 0;
11
12 // Iterate through all possible unique value combinations (a, b, c)
13 for (int valueA = 1; valueA <= 100; ++valueA) {
14 for (int valueB = 1; valueB <= 100; ++valueB) {
15 for (int valueC = 1; valueC <= 100; ++valueC) {
16 // Calculate sum of the three values
17 int tripleSum = valueA + valueB + valueC;
18
19 // Get frequencies of each value
20 int countA = frequency[valueA];
21 int countB = frequency[valueB];
22 int countC = frequency[valueC];
23
24 // Count how many values divide the sum evenly
25 int divisorCount = (tripleSum % valueA == 0) +
26 (tripleSum % valueB == 0) +
27 (tripleSum % valueC == 0);
28
29 // Only process if exactly one value divides the sum
30 if (divisorCount == 1) {
31 // Handle different cases based on duplicate values
32 if (valueA == valueB) {
33 // Case: two identical values (a, a, c)
34 // Choose 2 from countA and 1 from countC
35 totalTriplets += 1LL * countA * (countA - 1) * countC;
36 } else if (valueA == valueC) {
37 // Case: two identical values (a, b, a)
38 // Choose 2 from countA and 1 from countB
39 totalTriplets += 1LL * countA * (countA - 1) * countB;
40 } else if (valueB == valueC) {
41 // Case: two identical values (a, b, b)
42 // Choose 1 from countA and 2 from countB
43 totalTriplets += 1LL * countA * countB * (countB - 1);
44 } else {
45 // Case: all three values are different (a, b, c)
46 // Choose 1 from each count
47 totalTriplets += 1LL * countA * countB * countC;
48 }
49 }
50 }
51 }
52 }
53
54 return totalTriplets;
55 }
56};
57
1/**
2 * Counts the number of valid triplets where exactly one element divides the sum
3 * @param nums - Array of numbers (values between 1 and 100)
4 * @returns Number of valid triplets
5 */
6function singleDivisorTriplet(nums: number[]): number {
7 // Count frequency of each number (1 to 100)
8 const frequency: number[] = Array(101).fill(0);
9 for (const num of nums) {
10 frequency[num]++;
11 }
12
13 let totalTriplets = 0;
14
15 /**
16 * Helper function to check if divisor divides dividend
17 * @param dividend - The number to be divided
18 * @param divisor - The number to divide by
19 * @returns 1 if divisor divides dividend, 0 otherwise
20 */
21 const isDivisible = (dividend: number, divisor: number): number => {
22 return dividend % divisor === 0 ? 1 : 0;
23 };
24
25 // Iterate through all possible triplet values (a, b, c)
26 for (let a = 1; a <= 100; a++) {
27 for (let b = 1; b <= 100; b++) {
28 for (let c = 1; c <= 100; c++) {
29 // Calculate sum of the triplet
30 const sum = a + b + c;
31
32 // Count how many elements in the triplet divide the sum
33 const divisorCount = isDivisible(sum, a) + isDivisible(sum, b) + isDivisible(sum, c);
34
35 // Only process if exactly one element divides the sum
36 if (divisorCount === 1) {
37 // Handle cases based on duplicate values in the triplet
38 if (a === b && b === c) {
39 // All three values are the same: choose 3 from frequency[a]
40 totalTriplets += frequency[a] * (frequency[a] - 1) * (frequency[a] - 2);
41 } else if (a === b) {
42 // a and b are the same: choose 2 from frequency[a] and 1 from frequency[c]
43 totalTriplets += frequency[a] * (frequency[a] - 1) * frequency[c];
44 } else if (a === c) {
45 // a and c are the same: choose 2 from frequency[a] and 1 from frequency[b]
46 totalTriplets += frequency[a] * (frequency[a] - 1) * frequency[b];
47 } else if (b === c) {
48 // b and c are the same: choose 2 from frequency[b] and 1 from frequency[a]
49 totalTriplets += frequency[b] * (frequency[b] - 1) * frequency[a];
50 } else {
51 // All three values are different: choose 1 from each frequency
52 totalTriplets += frequency[a] * frequency[b] * frequency[c];
53 }
54 }
55 }
56 }
57 }
58
59 return totalTriplets;
60}
61
Time and Space Complexity
The time complexity is O(M^3)
, where M
is the number of distinct elements in the array nums
(which corresponds to the size of the Counter dictionary). This is because we have three nested loops, each iterating through all unique values in the Counter dictionary. Even though the reference mentions "the range of elements," the actual implementation iterates through the distinct elements present in nums
, not the entire possible range.
The space complexity is O(M)
, where M
is the number of distinct elements in nums
. This space is primarily used to store the Counter dictionary cnt
, which maintains a count for each unique element in the input array.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Overcounting When Values Are Equal
The most critical pitfall in this problem is incorrectly handling cases where some values in the triplet are equal. When two or three values are the same, naive multiplication of frequencies leads to overcounting because we're counting the same index multiple times.
Incorrect approach:
# This would count the same index twice when value_a == value_b if value_a == value_b: result += count_a * count_a * count_c # WRONG!
Why it's wrong: If value_a == value_b
and both refer to the same value with frequency count_a
, we need to choose 2 distinct indices from count_a
positions. Simply multiplying count_a * count_a
would allow choosing the same index twice.
Correct approach:
if value_a == value_b: # Choose 2 distinct indices from count_a positions result += count_a * (count_a - 1) * count_c
2. Missing the Case Where All Three Values Are the Same
Another subtle pitfall occurs when value_a == value_b == value_c
. The current code handles this correctly through the first condition (value_a == value_b
), but developers might accidentally write separate logic that double-counts this scenario.
Potential mistake:
# Adding extra conditions that overlap if value_a == value_b == value_c: result += count_a * (count_a - 1) * (count_a - 2) # Then also checking value_a == value_b separately would double count!
Solution: The existing cascading if-elif structure correctly handles this case without duplication. When all three values are equal, only the first condition (value_a == value_b
) executes, computing count_a * (count_a - 1) * count_a
.
3. Integer Division vs Modulo Confusion
Some developers might confuse the divisibility check and use integer division instead of modulo:
Incorrect:
# This checks if division result is non-zero, not divisibility
divisor_count = sum(
triplet_sum // value != 0 # WRONG!
for value in (value_a, value_b, value_c)
)
Correct:
# Modulo operator checks if remainder is zero (evenly divisible)
divisor_count = sum(
triplet_sum % value == 0
for value in (value_a, value_b, value_c)
)
4. Not Considering Order Matters for Index Triplets
The problem asks for ordered triplets of indices (i, j, k)
. Some might think combinations are needed and divide by 6 (3!), but the counting logic already handles ordered triplets correctly. Adding unnecessary division would undercount valid triplets.
Key insight: When choosing indices for values (a, b, c)
, the order of indices matters. The formula count_a * count_b * count_c
for distinct values already gives ordered selections, which is what we want.
Is the following code DFS or BFS?
void search(Node root) { if (!root) return; visit(root); root.visited = true; for (Node node in root.adjacent) { if (!node.visited) { search(node); } } }
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!