Блог пользователя tafsiruzzaman

Автор tafsiruzzaman, история, 23 месяца назад, По-английски

In the last Div.4 round, Problem 1985F - Final Boss witnessed numerous hacks, particularly targeting binary search solutions due to unexpected overflow in extreme cases.

A common challenge is determining the appropriate high-value for binary search: choosing it too high (e.g., 1e18) risks overflow, while choosing it too low (e.g., 1e9) may lead to incorrect answers. Finding the perfect static high-value for all cases can be tricky.

Here are two common fixes:

  1. Use a Very High Value and Handle Overflow with __int128(This approach can be complex and inefficient).
  2. Select a Suitable "High-Value" through Trial and Error (Not an ideal solution).

A More Sustainable Solution

As we know, binary search operates on a monotonic sequence such as [0, 0, 0, ..., 0, 1, ..., 1, 1, 1, 1]. Our objective is to find the last 0 or the first 1 in this sequence. If we successfully set the lo to any 0 zero and hi to any 1, our binary search will work perfectly. Now instead of relying on a static high-value for all cases, we can dynamically determine the minimum high-value necessary. This method involves starting with a low value and increasing it logarithmically until we find a suitable high-value. This ensures that our binary search operates within a safe range. Here's how you can implement this:

long long lo = 0, hi = 1;
while(!check(hi)) hi *= 2; // Check returns either 0 or 1 based on hi.

At the end of the loop, hi will point to a potential minimum high-value. After that, you can write your regular code. You can check my solution to understand the technique better.

My Solution

Note: I learn this technique from fahimcp495. Forgive me for any mistake.

I hope this technique will be helpful for those who don't know about this technique. Happy coding!

  • Проголосовать: нравится
  • +75
  • Проголосовать: не нравится

»
23 месяца назад, скрыть # |
 
Проголосовать: нравится +11 Проголосовать: не нравится

Or one can simply put a break statement when the sum reaches beyond the desired answer (Specifically for yesterday's problem F)

»
23 месяца назад, скрыть # |
 
Проголосовать: нравится +2 Проголосовать: не нравится

Since you are talking about overflow issues, it is recommended to use mid = lo + (hi-lo)/2 , as lo + hi can exceed the int or long long int range, but hi-lo is guaranteed to stay in the range :)

»
23 месяца назад, скрыть # |
 
Проголосовать: нравится +15 Проголосовать: не нравится

You can optimise it more:

    while(!check(hi)) { lo = hi; hi *= 2; }
»
23 месяца назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

Pardon my lack of understanding, but when you say overflow, are you referring to "mid" or something else? If it is mid, shouldn't writing mid = low + (high-low)/2 be enough?

»
23 месяца назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

crazy, so simple and useful

»
23 месяца назад, скрыть # |
 
Проголосовать: нравится +10 Проголосовать: не нравится

What if the values of check(hi) were something like this : $$$0,0,0,\ldots,0,0,1$$$ where the only valid hi would be $$$2^{63}-1$$$?

Then, following this general approach, the hi would be $$$2^0, 2^1, \ldots, 2^{61}, 2^{62}$$$. But now after $$$2^{62}$$$ it would overflow when you multiply it by 2?

  • »
    »
    23 месяца назад, скрыть # ^ |
     
    Проголосовать: нравится 0 Проголосовать: не нравится

    I think usually problem setters aren't psychics who try to annoy the participants, and it's CP so yes this won't happen, but if it did a single if would suffice to check for not overflow

»
23 месяца назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

or you can

Spoiler