← All problems

Set Multiples

TopCoder · SRM 505 (Div. 1) · 500 Pt

Problem

Let SS and TT be two sets of integers. We say that TT is a multiple of SS if, for every integer xSx \in S, there is an integer yTy \in T such that yy is a multiple of xx (that is, y=kxy = kx for some integer kk).

We are given four integers A,B,C,DA,B,C,D such that 1AB<CD10101 \leq A \leq B \lt C \leq D \leq 10^{10}. Let SS be the set of integers {x:AxB or CxD}\{ x : A \leq x \leq B \text { or } C \leq x \leq D\}. We would like to find the smallest subset TT of SS that is also a multiple of SS. Output the size of this set TT.

Note: SS is a subset of SS, and SS is a multiple of SS, so a solution always exists.

Initial Observations
  1. Set union
  2. Bitset
  3. Greed
  4. DP
  5. Graph (edges from divisors to multiples)
  6. Closure under an operation
  7. Intervals
  8. Matrix
  9. Primes, Prime Factorization, Seive
  10. SQRT factorization and SQRT tricks
  11. Tree
  12. Want logarithmic time solution

Notation: We will use SS to denote the given set, and TT to denote the "goal" or optimal set that satisfies the constraints. We say we "choose" (or "select") an integer xx in an algorithm if it results in xTx \in T when the algorithm is completed.

IdeaCharacterization and Greedy AlgorithmNO
Work an Example

When I first read this problem, I tried to look at examples in order to find a pattern. This problem deals with contiguous intervals and of divisibility of numbers; both of these subjects imply a high degree of "structure" to the problem. So the first goal was to try to understand this structure.

Based on the initial observations and looking at examples, we come to the following (pretty easy) observation.

Key Observation

For any integer xx in our set SS, we only need to use the largest multiple of xx when choosing TT (our optimal goal set).

Proof.

If some integer xSx \in S is chosen, but there is some other integer xSx' \in S so that x>xx' > x and xx' is a multiple of xx, then we can simply replace xx with xx', and we get another valid solution.

This observation is fairly easy to come up with, and it leads to a fairly simple "greedy" algorithm to construct a solution.

Algorithm
<code class="py">
Let T = {}
for x = D downto 1:
  if (A <= x<=B or C<=x<=D):
    if (no multiples of x exist in T):
      add x to T
	</code>

That is, we consider the xx in descending order, and we greedily "select" a number xx if there is no other multiple of xx already taken in TT. Obviously, since D1010D \approx 10^{10}, this algorithm is completely inefficient. But it does yield a good characterization of the optimal solution TT. We can now use this to prove the correctness of other ideas/algorithms, by showing that they return a set TT that is equivalent to the one returned by this solution.

Lesson

Often, it is easy to prove that something "greedy" works. Once we have that, we know that this train of thought will likely be fruitful and that we should avoid trying other approaches (for example, DP). The hard part is to understand the structure of the problem well enough to find a nice efficient solution.

Focus on a Condition

Now that we know that a greedy solution works, we need to exploit patterns and structure in the problem in order to find an efficient greedy algorithm. If we were given an arbitrary set SS, this problem would be extremely hard (I suspect); but since SS has a nice structure (the union of two intervals), I expect that we can use this to more easily find the optimal solution. The following ideas will attempt to exploit this.

IdeaDividing IntervalsTLE

In Idea 1, we were able to characterize how the optimal TT would look, and we came up with a greedy (but slow) way to construct TT. Next, we inspect this algorithm and try to learn more about the structure of the problem. By inspection (especially by looking at examples), we come up with another neat observation.

Key Observation

Every element x>D2x \gt \frac{D}{2} in SS must be chosen.

Proof.

If xSx \in S and x>D2x \gt \frac{D}{2} then any multiple kx2x>Dkx \geq 2x > D (if k2k \geq 2). So, no larger multiples of xx are in SS, and so xx must be chosen in TT to satisfy the definition of "set multiples".

Work an Example

This observation may or may not be considered "obvious", but it's easy to see if you try to run the greedy algorithm from Idea 1 on any reasonable example. So it's good to look at examples.

From Experience

For me, I had some intuition about these types of problems, so this observation came pretty naturally to me. See the Related Problems section for other problems related to divisibility. Gaining experience in dealing with "number-theoretic" problems will help in the future.

Moving on, we can make similar observations to this one. We can ask the questions: "What if CD2C \leq \frac{D}{2}? What if C>D2C > \frac{D}{2}?".

Lemma.

If CD2C \leq \frac{D}{2} then the optimal algorithm will choose all of the elements between D2+1\left\lfloor\frac{D}{2}\right\rfloor + 1 and DD. Moreover, no other elements need to be chosen.

Proof.

This really is a corollary to the first Key Observation. If CD2C \leq \frac{D}{2}, then we already know that all numbers between D2+1\left\lfloor\frac{D}{2}\right\rfloor + 1 and DD must be chosen (this is precisely the Key Observation).

We now show that the greedy (optimal) algorithm (from Idea 1) would not choose any other elements. In particular, we show that for all xD2x \leq \frac{D}{2}, there is an integer yy between D2+1\left\lfloor\frac{D}{2}\right\rfloor + 1 and DD that is a multiple of xx. (Note: this is not very hard to prove, and the reader is encouraged to do so independently.)

Take any integer xD2x \leq \frac{D}{2}. Then consider the largest k1k \geq 1 such that kxD2kx \leq \frac{D}{2}. Then, by construction, we have that (k+1)xD2+1(k+1)x \geq \left\lfloor\frac{D}{2}\right\rfloor + 1 (since kxkx was the largest multiple smaller than this). And since xD2x \leq \frac{D}{2} and kxD2kx \leq \frac{D}{2}, we therefore have that (k+1)x=(kx+x)D2+D2=D(k+1)x = (kx + x) \leq \frac{D}{2} + \frac{D}{2} = D. So this multiple (k+1)x(k+1)x is precisely in the range of already chosen numbers. Hence xx always has a multiple in this range.

Thus, the Optimal Greedy Algorithm would never select another number. So this is optimal.

We have just shown that if CD2C \leq \frac{D}{2} then the optimal solution (i.e.: the algorithm above) will select exactly those numbers between D2+1\left\lfloor\frac{D}{2}\right\rfloor + 1 and DD, which will suffice.

Key Question

What if C>D2C > \frac{D}{2}?

In these cases we must select all of C..DC..D (since none of these are multiples of each other), but we may have to select some additional numbers between AA and BB. Again, we argue that we would only need to select numbers greater than B2\frac{B}{2} but not greater than BB (since all numbers B2\leq \frac{B}{2} have some multiple in that range). But even so, we don't select them all, because many of these will have some multiple in the already chosen set between CC and DD.

Simplify

In the case where C>D2C > \frac{D}{2} the previous observation implies that we can simplify the problem. In particular, the problem reduces to being able to find which numbers between B2+1\left\lfloor\frac{B}{2}\right\rfloor + 1 and BB have some multiple between CC and DD.

Generate & Test

I had the following idea for an algorithm intuitively. I wasn't sure whether it would work, but it seemed like a natural thing to try.

Algorithm

Since these are contiguous intervals ([B2+1..B][\left\lfloor\frac{B}{2}\right\rfloor + 1 .. B] and [C..D][C..D]), here is one way to proceed.

For a fixed integer k>1k>1, we consider the interval (Ck..Dk)\left(\left\lceil \frac{C}{k} \right \rceil .. \left\lfloor \frac{D}{k} \right \rfloor\right). It turns out that every integer xx in this range has a multiple kxkx in the original range (C..DC..D) (Proof omitted). Using a form of "Brute-force", we therefore "try all possible k>1k>1". Consider C2..D2\left\lceil \frac{C}{2} \right \rceil .. \left\lfloor \frac{D}{2} \right \rfloor, then C3..D3\left\lceil \frac{C}{3} \right \rceil .. \left\lfloor \frac{D}{3} \right \rfloor, ..., and so on, keeping track of which of these intervals overlap with [B2+1..B][\left\lfloor\frac{B}{2}\right\rfloor + 1 .. B], and marking all integers in these intervals as "factors" / "not-chosen" (Implementation details omitted).

Assuming we figure out the implementation details, this will yield an algorithm that correctly marks all numbers in [B2+1..B][\left\lfloor\frac{B}{2}\right\rfloor + 1 .. B] that have a multiple in [C..D][C..D]. By taking the union of [B2+1..B][\left\lfloor\frac{B}{2}\right\rfloor + 1 .. B] and [C..D][C..D], minus the marked numbers, we will have all the numbers that would be chosen in an optimal solution.

Work Backwards

In our Problem Simplification, we said we were looking for all the numbers in [B2+1..B][\left\lfloor\frac{B}{2}\right\rfloor + 1 .. B] that have some multiple in [C..D][C..D]. The above algorithm, however, solves this problem in reverse: we consider all numbers in the range [C..D][C..D] and find all factors of them within the ranges of [B2+1..B][\left\lfloor\frac{B}{2}\right\rfloor + 1 .. B].

This is an example of "working backwards". We are looking at the inverse problem, which can sometimes be easier to solve.

Key Question

How efficient is this? How large can k"``k" get in the above algorithm?

Work an Example

At this point, I tried some examples. A very nice example turned out to be C=200,D=256C = 200, D = 256, AA and BB can be anything reasonable. (I encourage the reader to try this with increasing values of kk). We realize we don't have a bound on kk, except that kDk \leq D. But we do notice after a while that these "intervals" stop being disjoint.

For example, take k=4k=4 and then k=5k=5. For k=4k=4, the interval is 2004..2564\frac{200}{4} .. \frac{256}{4} which is [50..64][50 .. 64]. But for k=5k=5 (omitting the calculations), we get the interval [40..51][40..51]. Notice, the upper-bound (5151) of the second interval overlaps with the lower-bound (5050) of the first interval. The reader may verify that this continues to be the case for all k5k \geq 5 for this example (I only tried until k=10k=10). This yields a very nice pattern: for large enough KK, there is some vv such that all numbers v\leq v are covered by the numbers in the intervals Ck..Dk\left\lceil \frac{C}{k} \right \rceil .. \left\lfloor \frac{D}{k} \right \rfloor, over all kKk \geq K.

This observation is pretty nice, because it shows that, after a while we can just ignore all small numbers (and assume they have a multiple). When does this happen exactly?

Key Observation

Well we want to know when Ck..Dk\left\lceil \frac{C}{k} \right \rceil .. \left\lfloor \frac{D}{k} \right \rfloor overlaps with Ck1..Dk1\left\lceil \frac{C}{k-1} \right \rceil .. \left\lfloor \frac{D}{k-1} \right \rfloor. This happens if and only if DkCk1\left\lfloor \frac{D}{k} \right \rfloor \geq \left\lceil \frac{C}{k-1} \right \rceil. For simplicity, we relax the "floor" and "ceiling" symbols, and just solve the inequality directly, and we get:

kDDCk \geq \frac{D}{D-C}

So, in particular, whenever kDDCk \geq \frac{D}{D-C}, the intervals will be overlapping. Repeating this argument (essentially by induction) shows that, once this happens, this set of intervals will cover all numbers from some vv down to 1.

Observation.

This algorithm works well whenever DCD-C is large in comparison to DD.

Here are the implementation details fleshed out into pseudo-code. (Note: This is equivalent to Algorithm 2 above)

Algorithm
<code class="py">
SetMultiples1(A,B,C,D):
    if C <= D/2:
        # (Implicitly) Set T = {D,D-1,D-2,...,floor(D/2) + 1}
        return ceil(D/2)  # We only want the size of T
    else:
        Let marked = {}    be an empty set
 
        # The original ranges that define S
        Let upperRange = (C,D)
        Let lowerRange = (max(floor(B/2) + 1,A), B)
 
        for all k = 2..INFINITY:
            currentRange = (ceil(C/k), floor(D/k))
            prevRange    = (ceil(C/(k-1)), floor(D/(k-1)))
 
            if intersection(currentRange, prevRange) isnt EMPTY:        # They overlap
                marked.insert(x) : for all x from floor(D/k) downto 1
                break
            else:
                marked.insert( intersection(currentRange, lowerRange) )
 
        return upperRange.size() + lowerRange.size() - marked.size()
	</code>

In the above pseudo-code we represent intervals as (lower bound, upper bound ) pairs. For example, the variables upperRange, lowerRange, and currentRange all represented intervals.

As proven earlier, this algorithm works fine when DCD-C is large. Eventually the intervals (Ck..Dk)(\frac{C}{k} .. \frac{D}{k}) will begin to overlap, and we can break out of the loop early. In the next Idea Section, we show how to handle the case when DCD-C is small

IdeaDirect FactorizationAC

In the idea section above, we described an algorithm that is guaranteed to terminate quickly whenever CC is much smaller than DD (that is, whenever DCD-C is large). We won't review that algorithm now, but we recall that it runs in Θ(DDC)\Theta(\frac{D}{D-C}) time-complexity. We would now like to answer the following question:

Key Question

How can we solve the problem when CC is closer to DD?

Simplify

As an example, we can consider the extreme case when C==DC==D. Can we think of a good algorithm in this case?

Algorithm

If C==DC==D then we take CC and consider all the elements between A..BA..B. Similarly to as proven (in the previous Idea Section), we only need to take items greater than B2\frac{B}{2} but not greater than BB. Of all these numbers, we can ignore any of those that are factors of DD. This would yield the optimal set (since all numbers chosen are their own "maximal" multiples; see Idea Section 1).

Key Question

How efficient would this be?

From Experience

This analysis of efficiency requires some familiarity with number theory and factorization. In particular, one should recall the "Square-Root Trick" for factorization, that shows that it takes O(N)O(\sqrt{N}) time to find all factors of a number NN. In our case, since D1010D \leq 10^{10}, we check O(D)105O(\sqrt{D}) \approx 10^{5} numbers.

Also, the number of actual factors is much smaller than this. Most numbers under 101010^{10} have under 100 factors (this is a rough estimate).

So, overall, this is reasonable.

Key Observation

The algorithm works in general. Given any C>D2C>\frac{D}{2} and DD, we can manually factorize all numbers yy between CC and DD, marking each factor as needed. We can then take the intervals (max(B2+1,A)..B)(\max(\left\lfloor\frac{B}{2}\right\rfloor + 1,A) .. B) and C..DC .. D and subtract any marked numbers. This will yield an optimal solution.

The overall time complexity of this is Θ(D(DC))\Theta(\sqrt{D} (D-C)), since we have to manually check the factor of DC+1D-C+1 numbers, which takes O(D)O(D) time each.

Observation.

This algorithm runs quickly if DCD-C is small.

From Experience

In the previous Idea, we found an algorithm that is efficient whenever DCD-C is large. We have also shown that the Algorithm from this current Idea is efficient whenever DCD-C is small. From experience (e.g.: from Calculus) or by "Symmetry" we know that it's best to find a "balance" between these two solutions (i.e.: get their efficiencies as close to each other as possible).

This is similar to the "Square Root Trick" for factorization: given two numbers that multiply to nn, at least one of the numbers must be smaller than n\sqrt{n}, etc. So we can factorize nn in n\sqrt{n} time. This principle applies very often when solving problems: we often have two conflicting functions we want to optimize and we can prove that one of those functions will be small whenever the other is large. Usually if these are functions in some number nn, one of the functions will be O(n)O(\sqrt{n}) or something similar.

Anyway, with this in mind, we have the following problem.

Lemma.

Suppose we treat DD is a constant and let CC be variable. In particular, consider the difference DCD-C.

Recall that we have:

  • an algorithm that runs well when DCD-C is large (in Θ(DDC)\Theta(\frac{D}{D-C}) time)
  • another algorithm that runs well when DCD-C is small (in Θ(D(DC))\Theta(\sqrt{D} (D-C)) time)

Then we can combine these algorithms to get a (worst-case) O(D34)O(D^{\frac{3}{4}}) algorithm.

Proof.

To "combine" these algorithms, we mean: given a CC and DD, we choose whichever algorithm is faster for our particular input (DCD-C). If we do this combined algorithm, we get a time complexity F(C,D)F(C,D) defined as follows:

F(C,D)=min{DDC,D(DC)}F(C,D) = \min\left\{ \frac{D}{D-C}, \sqrt{D} (D-C) \right\}

For simplicity, let's assume that DD is a constant and we vary DCD-C. So we let x:=DCx := D-C be a variable, and we can write all of these functions in terms of xx. So we really have:

F(x)=min{Dx,xD}F(x) = \min\left\{ \frac{D}{x}, x\sqrt{D} \right\}

Now we are taking the minimum of two functions in xx, one that is increasing and one that is decreasing, and we would like to find out "how bad can this get". In particular, we want to find the maximum point of this function. Using techniques from calculus, or just by intuitive reasoning, we know this is maximized whenever the two are equal. Hence, we can solve for xx by setting:

Dx=xD\frac{D}{x} = x\sqrt{D}

Solving for xx (math omitted), we get that x=D14x = D^{\frac{1}{4}}. This means, whenever the difference x=DCx = D-C is more than D14D^{\frac{1}{4}}, we choose the first algorithm; and whenever the difference is less than D14D^{\frac{1}{4}}, we choose the second algorithm. In the worst case we will have:

F(D14)=D34F(D^{\frac{1}{4}}) = D^{\frac{3}{4}}

So, altogether we have a Θ(D34)\Theta(D^{\frac{3}{4}}) algorithm, as desired.

Observation.

The lemma above actually gives us an algorithm for solving the problem, based on the two different concepts we have developed. See the Solution Summary (in the Review Section) below for a concise overview of the solution.

Review

Summary

All-in-all, this problem required some interesting greedy observations to solve it efficiently.

We first observe that, for a given integer xx, we "choose" xx to be in our final set if and only if xx has no larger multiples in SS (for simplicity, we will call all of these numbers "maximal" numbers). This always yields an optimal solution (proven above). Hence, we simply want to find all "maximal" numbers xx in the ranges AxBA \leq x \leq B or CxDC \leq x \leq D.

To find an efficient algorithm, we can exploit the fact that SS consists of two disjoint intervals. This actually makes the problem much simpler and more "regular". For example, we can make the following observation: Between CC and DD (inclusive), we always need to select the numbers above D2\frac{D}{2} (since none of these are "maximal"), if CD2C \leq \frac{D}{2}. In fact, with a bit more work, we can show that, if C<D2C \lt \frac{D}{2} then these are the only numbers we need to select (we can prove that any smaller number has a multiple in this set). So we can assume that C>D2C > \frac{D}{2}. Similarly, we can assume that A>B2A > \frac{B}{2}, since an analogous truth holds for AA and BB.

At this point, we can assume CC is somewhat "close" to DD (in particular, C>D2C > \frac{D}{2}). To solve this, I tried one fairly intuitive algorithm: Look at the end-points of the interval (C,D)(C,D) and divide them by 2. Then every integer xx in this new interval (C2..D2)(\frac{C}{2} .. \frac{D}{2}) has a corresponding multiple 2x2x in the original interval (C,D)(C,D). So all such numbers are clearly not maximal. A similar fact holds for (C3..D3)(\frac{C}{3}..\frac{D}{3}), (C4..D4)(\frac{C}{4}..\frac{D}{4}), ..., and so on. So, the algorithm is to try all these intervals, and "mark" the numbers xx that we find. One convenient fact about this is that most of these intervals are disjoint, so we don't need to explicitly mark the xx, but we just count how many xx are in the range and also in the range B2..B\frac{B}{2} .. B. When this algorithm terminates, the size of our final set will be (DC+1)+(Bmax(A1,B2)){marked numbers}(D-C+1) + (B - \max(A-1,\frac{B}{2})) - |\{\text{marked numbers}\}| (the numbers in our given ranges, minus the number of "marked" factors).

By inspection, the intervals (Ck..Dk)(\frac{C}{k}..\frac{D}{k}) will eventually intersect with each other. Actually, you can prove that, after a certain kk, the intervals will cover all numbers from Dk\frac{D}{k} down to 1. So we can actually "exit" early. This happens whenever k>DDCk > \frac{D}{D-C} (we proved this in Idea 2), so it happens more quickly whenever DCD-C is large (or when DD is small, but we can't assume this).

By noting that this algorithm works when DCD-C is large, we can ask the next intuitive question: "What about when DCD-C is small?". To answer this, we realize that we can just simply explicitly find all the factors of the numbers between CC and DD. We mark these factors that overlap with the chosen part of the A..BA..B interval. This runs in O((DC)D)O((D-C)\sqrt{D}) time, since there are about DCD-C numbers in the range C..DC..D and each number requires O(D)O(\sqrt{D}) time to find its factors. So, this algorithm is clearly better when DCD-C is smaller.

We now have two "complimentary" algorithms, the first having O(DDC)O(\frac{D}{D-C}) time-complexity, and the second having O((DC)D)O((D-C)\sqrt{D}) time-complexity. We can combine these algorithms by always choosing the "minimum" one; that is, we choose whichever algorithm runs faster for our given input. We then have an algorithm that has time-complexity: O(min{DDC,(DC)D})O( \min\{\frac{D}{D-C}, (D-C)\sqrt{D}\} ). Using some intuitive arguments (or by calculus) this function has its worst-case complexity of D34D^{\frac{3}{4}} when (DC)D14(D-C) \approx D^{\frac{1}{4}}. Since D1010D \leq 10^{10} this just (barely) runs correctly under the time-limit for TopCoder.

Accepted.

  1. We select an integer xx to be in our set if and only if it has no other multiples in the set SS. This was pretty intuitive to come up with, but it was helpful when trying to find a fast algorithm.

  2. Given a single interval S=(C..D)S = (C..D) we MUST select the numbers greater than D2\frac{D}{2} in an optimal solution TT.

  3. If we take an interval of integers from CC to DD, and we divide both end-points by an integer kk, then every integer in this new interval (Ck to Dk\frac{C}{k} \text{ to } \frac{D}{k}) will have a multiple kxkx in the original range. This formed the basis of our first algorithm

  4. Take an interval of integers from CC to DD. Consider the intervals (C2..D2)(\frac{C}{2} .. \frac{D}{2}), (C3..D3)(\frac{C}{3} .. \frac{D}{3}), (C4..D4)(\frac{C}{4} .. \frac{D}{4}), ..., (Ck1..Dk1)(\frac{C}{k-1} .. \frac{D}{k-1}), (Ck..Dk)(\frac{C}{k} .. \frac{D}{k}). Then (Ck1..Dk1)(\frac{C}{k-1} .. \frac{D}{k-1}) will overlap with (Ck..Dk)(\frac{C}{k} .. \frac{D}{k}) if and only if kDDCk \geq \frac{D}{D-C}. This showed that we could quit this algorithm early whenever kk gets large enough. In particular, we can end earlier if DCD-C is larger.

  5. It takes O(D)O(\sqrt{D}) time to find all the factors of a number. Hence, all factors of all numbers between CC and DD can be checked in O((DC)D)O((D-C)\sqrt{D}) time. This gave us the basis of our second algorithm. This works whenever DCD-C is small.

  6. Consider the time-complexity function F(x)=min(Dx,xD)F(x) = \min(\frac{D}{x}, x\sqrt{D}). This function achieves its maximum (worst-case) value of F(x)=D34F(x) = D^{\frac{3}{4}} when xD14x \approx D^{\frac{1}{4}}. Since Dx\frac{D}{x} is decreasing and xDx\sqrt{D} is decreasing, we can show that the worst-case point occurs when these two functions are equal. So we set Dx=xD\frac{D}{x} = x\sqrt{D} and solve for xx. This gives us the formula.

  7. This is largely a greedy / ad-hoc problem. These kinds of problems usually require finding good observations about the structure of the problem. It often helps to look at examples to help find these patterns. Also, once we started to find more and more observations, it was good to go back and try them on examples to get a good understanding of how they work.

  8. Once we found a greedy algorithm, we realize that we can make it efficient by exploiting the structure of the problem. In particular, the input set SS will always consist of two disjoint intervals. (This might have been much harder if we were given an arbitrary set SS.) Another way to exploit the conditions is to notice that D1010D \leq 10^{10}. This implies that a Θ(D)\Theta(\sqrt{D}) algorithm should be possible, since D105\sqrt{D} \approx 10^5 is right around a "reasonable" input-range.

  9. If you've never seen "the Square Root Trick" for factorization, then this problem becomes much harder. In addition (see the Learning Points), I've learned from experience that this "Square Root Trick" applies (in principle) to other kinds of problems. This helped us to analyse the running time achieved when combining the two algorithms. You also need some experience and intuition in basic Number Theory in general to be able to solve this problem.

  10. A nice example was the case when C=DC=D. This was an extreme case (i.e.: when DCD-C was 0). By finding an algorithm that works here, we could easily generalize it to work for other small DCD-C.

  11. Most of the algorithms we came up with were pretty "intuitive". The way we solved this problem was by writing out these algorithms ("testing" them), and by finding out exactly where they fail. In particular, we learned that these algorithms largely depended on the parameter DCD-C, so then we could solve the problem by focusing on this variable.

  12. Instead of finding all multiples of a given number xx, we focused on finding the factors of numbers yy.

Given (n+1)(n+1) unique numbers between 11 and 2n2n (for some positive integer nn), prove that there exists a pair of elements xx and yy such that yy is a multiple of xx. This is a nice math problem that I remembered when reasoning about divisibility.

UVa 11466 - Largest Prime Divisor. A problem on finding large prime factors. Take note of the constraints and recall out "Learning Point".

UVa 11960 - Divisor Game. This problem shows that most numbers have a small number of factors. It's also a nice illustration of working backwards.

Codeforces Round #257 (Div. 1) C - Jzzhu and Apples. Another problem dealing with number theory and divisibility. It is also an "optimization" problem where we are trying to maximize something with respect to the numbers.

NCPC 2008 Problem J: Just a Few More Triangles! Here is a harder number theory problem that I don't know how to solve. It's more practice. (I found this problem on Codeforces Gym)

Coding Contest Byte: The Square Root Trick. A nice article describing Square Root decomposition and related problems.