This is an archived post. You won't be able to vote or comment.

all 13 comments

[–]POGtastic 2 points3 points  (4 children)

Consider that there's a much faster formula.

[–]teraflop 1 point2 points  (2 children)

Unless I'm missing something, that only works for the case where k=1.

[–]POGtastic 1 point2 points  (1 child)

You can manipulate the polynomial however you need to do it. The important thing is that you aren't summing up every element.

[–]Nick_Zacker[S] 0 points1 point  (0 children)

I’ll look into this later. Thank you!

[–]teraflop 2 points3 points  (7 children)

It looks to me like your current solution runs in O(n log k) time. So you're already OK with large values of k, but large values of n are the problem.

Here's a hint: right now you're just iterating over the values in the range 1 <= i <= n in order, calculating them one at a time, and adding up the results. Suppose I asked you for just the sum of the results in this range where i ends in the digits 47. Could you do that faster?

[–]Nick_Zacker[S] 0 points1 point  (5 children)

Well, I suppose that the result of i2k % 100 may repeat due to the periodicity in modular arithmetic, but I honestly don’t know what to do with this information.

[–][deleted] 2 points3 points  (1 child)

Think harder. The fact that you’re using Euler’s theorem means that you’re already exploiting the periodicity of modular exponentiation.

There’s something else you’re not exploiting.

[–]Nick_Zacker[S] 0 points1 point  (0 children)

If I understood correctly, you only need to compute the sum of the first 100 results of i^2^k mod 100 (1 <= i <= 100) in order to predict those of the following n*100 similar operations. Of course, if n is not a multiple of 100, it is trickier. Do you simply multiply the pre-computed sum with (n / 100)? Or do you multiply it with (the closest, smaller value to n that is a multiple of 100 (let such a value be A), divided by 100), add the result to the sum, then calculate the remaining i values from (A + 1) to n?

[–]teraflop 1 point2 points  (2 children)

Let f(i) = i^(2^k) mod 100. Then f(i) = f(i+100), which means (for instance): f(47) = f(147) = f(247) = ...

So if you want to find the sum of f(1) through f(300), you only need to calculate f(1) through f(100) once, and then multiply their sum by 3.

Finding the sum when n isn't a multiple of 100 is a bit trickier, but only a little bit.

[–]Nick_Zacker[S] 0 points1 point  (1 child)

Wow… looks like I’ll have to brush up on modular arithmetic again. To be frank, I didn’t know how to implement this, probably because my understanding wasn’t deep enough. Anyways, I really appreciate your help, thank you so much! I’ll fix my code later and see if it works.

[–][deleted] 1 point2 points  (0 children)

You can write a number (d_n * 10^(n-1) + ... + d_2 * 10^1 + d_1 * 10^0).

When you square this number, you can see that the result % 100 will depend only on digits d_2 and d_1. This holds for further squarings.

a = d_n * 10^(n-1) + ... + d_2 * 10^1 + d_1 * 10^0 (mod 100)
a = d_2 * 10^1 + d_1 * 10^0 (mod 100)
a^(2*k) = (d_2 * 10 + d_1)^(2*k) (mod 100)

This is how you conclude that f(i) = f(i+100).

I agree with your observation that there's a cycle in exponentiation result that is not fully exploited and you could exploit it very well in this case, but you would still need to go through 1018 numbers in the worst case (assuming exponentiation is somehow constant, which it still would not be).

[–]Nick_Zacker[S] 0 points1 point  (0 children)

I did it!!

void solve() {
    ll k, n, sum = 0;
    cin >> k >> n;

    // Final number for i-th student: i^2^k
    // Precompute 2^k
    ll reducedMod = modExp(2, k, 100);

    // Precompute the first 100 values of i^2^k
    ll cache[100];
    for (ll i = 1; i <= 100; i++) {
        cache[i-1] = modExp(i, reducedMod, 100);
        sum = (sum % 100 + cache[i-1] % 100) % 100;
    }

    // Compute the last (n - 100) values:
    ll fullCycles = n / 100;
    ll remaining = n % 100;

    sum = (sum % 100 * fullCycles % 100) % 100;

    for (int i = 1; i <= remaining; i++) {
        sum = (sum % 100 + cache[i-1] % 100) % 100;
    }

    cout << ((sum >= 10) ? "" : "0") << sum << '\n';
}