You are given three ints: n, k, and m.
For each i between 1 and n, inclusive, Fox Ciel calculated the number i^(2^k - 1). ("^" denotes exponentiation.)
Return the sum of all numbers Fox Ciel calculated, modulo m.
题解
This problem has two parts. First we need to know that each element of the sum can be calculated in O(k) time. We have a power of the form i2k−1. If we use exponentiation by squaring, we can calculate a power in O(log(x)) time where x is the exponent. This time the exponent is O(2k) , the base 2 logarithm of 2k is k. This means that if we use exponentiation by squaring we will need O(k) time. Because the specific exponent is a power of 2 minus 1, there are also other methods we can use. For example: 2k−1=20+21+22+...2k−1. So we have: i20+21+22+...2k−1=(i20)(i21)(i22)...(i2k−1). Each i2k can be found by squaring i2k−1. Ultimately, it doesn't matter if we use this method that is specific to this problem because it is still O(k)
The second part of the problem is to realize that even with such an algorithm, we will need O(nk) time to find all n elements of the sum and add them together. For the constraints and the time limit that is too high, specially because of all the modulo operations we will need to do for each step.
If we want a faster solution we should try to look for a way to reuse the work spent in calculating the result for an i so that it can make the calculation for other elements easier. A nice way is to notice this: Imagine a number a=bc, a is the product of two integers. The exponentiation for a is : f(a)=a2k−1. Try this:
Exponentiation is distributive between products. Since we have to calculate f(i) for all the numbers in a range, it is likely that when we are about to calculate f(a) we already know the values of f(b) and f(c) , so we can just reuse those results and avoid an O(k) calculation.
We just need to materialize that idea into a solution. For each i in the range we need to find two factors pq such that i=pq. We should tell right away that there will be many numbers for which this is impossible: Prime numbers. If i is prime, we need to use the past method of calculating f(i) in O(k) time. There are 78498 prime numbers among the first 1000000 natural numbers. So in the worst case we will do the O(k) algorithm 78498 times. It is still much better than doing it 1000000 times.
Finally, in case i is composite, we need to quickly find two factors pq=i. We can just do all of this as we test if i is prime, just use the successive divisions, if we don't find a divisor p (and therefore q=ip is also a divisor so we have our pair of factors), then the number is prime. We can do better and use a Sieve of Eratosthenes, just with a small modification, when you find a prime number p , don't just strike down its multiples as composite numbers, also save p as a "first factor" of each of the composite numbers. Then we can use the look up for the first factor to find a divisor.
long get_ith_element(int i, int k, int m)
{
// calculate i ^ (2^k - 1) in O(k) time:
long p = i;
long q = p;
for (int j = 1; j < k; j++) {
q = (q * q) % m;
p = (p * q) % m;
}
return p;
}
int calc(int n, int k, int m)
{
// modified Sieve of Erathostenes, when the number is composite,
// first_factor[i] will return a prime number that divides it.
vector first_factor(n + 1, 1);
for (int i = 2; i <= n; i++) {
if (first_factor[i] == 1) {
// prime
first_factor[i] = i;
for (int j = i+i; j <= n; j += i) {
first_factor[j] = i;
}
}
}
// f(p*q) = f(p) * f(q) , because f(i) = i ^ (something)
vector dp(n + 1, 1LL );
long sum = 0;
for (int i = 1; i <= n; i++) {
if (first_factor[i] == i) {
dp[i] = get_ith_element(i,k,m);
} else {
dp[i] = (dp[first_factor[i]] * dp[i / first_factor[i]]) % m;
}
sum += dp[i];
}
return (int)(sum % m);
}