5

Question is as follows : Given two numbers n and k. For each number in the interval [1, n], your task is to calculate its largest divisor that is not divisible by k. Print the sum of all these divisors. Note: k is always a prime number. t=3*10^5,1<=n<=10^9, 2<=k<=10^9

My approach toward the question: for every i in range 1 to n, the required divisors is i itself,only when that i is not a multiple of k. If that i is multiple of k, then we have to find the greatest divisor of a number and match with k. If it does not match, then this divisor is my answer. otherwise, 2nd largest divisor is my answer.

for example,take n=10 and k=2, required divisors for every i in range 1 to 10 is 1, 1, 3, 1, 5, 3, 7, 1, 9, 5. sum of these divisors are 36. So ans=36.

My code,which works for a few test cases and failed for some.

#include<bits/stdc++.h>
using namespace std;
#define ll long long int

ll div2(ll n, ll k) {
if (n % k != 0 || n == 1) {
    return n;
}

else {
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            ll aa = n / i;
            if (aa % k != 0) {
                return aa;
            }
        }
    }
}
return 1;
}



int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int t;
cin >> t;

while (t--) {
    ll n, k;
    cin >> n >> k;
    ll sum = 0, pp;

    for (pp = 1; pp <= n; pp++) {
        //cout << div2(pp, k);
        sum = sum + div2(pp, k);
    }
    cout << sum << '\n';
 }

 }

Can someone help me where I am doing wrong or suggest me some faster logic to do this question as some of my test cases is showing TIME LIMIT EXCEED

after looking every possible explanation , i modify my code as follows:

#include<bits/stdc++.h>
using namespace std;
#define ll long long int

int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int t;
cin >> t;

while (t--) {
    ll n, i;
    ll k, sum;
    cin >> n >> k;

    sum = (n * (n + 1)) / 2;

    for (i = k; i <= n; i = i + k) {
        ll dmax = i / k;

        while (dmax % k == 0) {
            dmax = dmax / k;
        }
        sum = (sum - i) + dmax;

    }
    cout << sum << '\n';

}

}

But still it is giving TIME LIMIT EXCEED for 3 test cases. Someone please help.

  • Please include a [mcve]. We cannot help with code that we cannot see – 463035818_is_not_an_ai May 18 '20 at 10:34
  • Your problem statement is a bit fishy. "Note: k is always a prime number. " what is `k` ? That note is the only place where you mention it in the text – 463035818_is_not_an_ai May 18 '20 at 10:35
  • @idclev463035818 sorry sir, just typing mistake. –  May 18 '20 at 10:38
  • Out of curiosity: what's the contest site where you found that challenge? – Ralf Kleberhoff May 18 '20 at 11:39
  • Just one more hint: take n=200, k=7 as an example, using the brute-force program. You'll probably see a pattern there (numbers divisible by 7, by 49, ...). And then you need the creative thinking like Gauss did for the sum of the numbers from 1 to 100. Combining all that should lead you onto the solution path. – Ralf Kleberhoff May 18 '20 at 13:24
  • Can someone check my modified code? –  May 18 '20 at 15:06
  • Hint. The sum of the divisors for the numbers not divisible by `k` is `1+2+...+n - k*(1+2+...+n/k)`. Now what is the sum of the divisors for the numbers divisible by `k` and not `k^2`? – btilly May 18 '20 at 18:00
  • Note: `for (int i = 2; i * i <= n; i++) {` can overflow for `n` near `INT_MAX`. Suggest `for (int i = 2; i <= n/i; i++) {` – chux - Reinstate Monica May 19 '20 at 01:28
  • @btilly Can u please check my above-modified code( 2nd code). I am trying to do on ur logic. But still, it is giving TIME LIMIT EXCEED for 3 test cases. While the remaining cases are running fine. –  May 19 '20 at 04:58
  • @sushan You are definitely not working on my suggested logic. You are looping by `k` up to `n`. If `k=2` and `n` is a billion, that's not going to work very well. If you figure out my suggestion you'll have to only do `log(n)` calculations to get the answer. (And no I am not posting the answer because the whole point of this kind of challenge is to have you have something to figure out.) – btilly May 19 '20 at 06:36

3 Answers3

3

Like others already said, look at the constraints: t=3*10^5,1<=n<=10^9, 2<=k<=10^9.

If your test has a complexity O(n), which computing the sum via a loop has, you'll end up doing a t * n ~ 10^14. That's too much.

This challenge is a math one. You'll need to use two facts:

  • as you already saw, if i = j * k^s with j%k != 0, the largest divisor is j;
  • sum_{i=1}^t i = (t * (t+1)) / 2

We start with

S = sum(range(1, n)) = n * (n+1) / 2

then for all number of the form k * x we added too much, let's correct:

S = S - sum(k*x for x in range(1, n/k)) + sum(x for x in range(1, n/k))
  = S - (k - 1) * (n/k) * (n/k + 1) / 2

continue for number of the form k^2 * x ... then k^p * x until the sum is empty...

Ok, people start writing code, so here's a small Python function:

def so61867604(n, k):
    S = (n * (n+1)) // 2
    k_pow = k
    while k_pow <= n:
        up = n // k_pow
        S = S - (k - 1) * (up * (up + 1)) // 2
        k_pow *= k
    return S

and in action here https://repl.it/repls/OlivedrabKeyProjections

One Lyner
  • 1,964
  • 1
  • 6
  • 8
  • what is j,s here ?? –  May 18 '20 at 13:11
  • , Can u please elaborate a bit of your explanation by taking an example? –  May 18 '20 at 13:18
  • Sir can u please reply, sum=(n*(n+1))/2 is used only when numbers are from 1 to n . But here may be numbers in between are irregular. –  May 18 '20 at 15:09
  • Start with the full sum, then try to find the value you need to remove to correct it. – One Lyner May 18 '20 at 15:25
  • sir , after using sum as (n*(n+1))/2 at start , 7 out of 10test cases are passed. still 3 test cases are throwing TIME LIMIT EXCEED. –  May 18 '20 at 16:14
  • for the correction step, do not use a loop, this is a sum once again – One Lyner May 18 '20 at 21:02
  • Sir , but how to remove while loop . Before sum we have to calculate actual divisor for that i. –  May 19 '20 at 05:15
  • I added some more hints. I'll add the full algo in one or two days if you still need it. – One Lyner May 19 '20 at 08:52
  • @גלעדברקן Which code, I gave no code, just a hint! Your interpretation of my hint gives a wrong result. – One Lyner May 19 '20 at 14:46
  • Actually, my code was written at 05-19 01:20:08 (https://stackoverflow.com/revisions/8e672aec-0235-4d9d-9f3b-5ab2b683b3ef/view-source), in response to your initial answer given at 05-18, 12:32:39 (https://stackoverflow.com/revisions/c233fe87-f278-44bf-ac64-a844ce633add/view-source). That answer did not mention subtracting from n * (n + 1) / 2. – גלעד ברקן May 19 '20 at 16:12
  • My code there includes the line, `let toRemove = numJs * (numJs + 1) / 2 * (p - 1);` but there are two errors with it, as your current code shows - the `p` is supposed to be the original `k`, not the variant, which also removes the false need I identified to add back what I thought were double counted powers. So perhaps you can also give me a little more credit :) – גלעד ברקן May 19 '20 at 16:12
2

In itself this is more of a mathematical problem: If cur = [1..n], as you have already noticed, the largest divisor = dmax = cur is, if cur % k != 0, otherwise dmax must be < cur. From k we know that it is at most divisible into other prime numbers... Since we want to make sure that dmax is not divisible by k we can do this with a while loop... whereby this is certainly also more elegantly possible (since dmax must be a prime number again due to the prime factorization).

So this should look like this (without guarantee just typed down - maybe I missed something in my thinking):

#include <iostream>

int main() {
    unsigned long long n = 10;
    unsigned long long k = 2;

    for (auto cur_n = decltype(n){1}; cur_n <= n; cur_n++)
    {
        if (cur_n % k != 0) {
            std::cout << "Largest divisor for " << cur_n << ": " << cur_n << " (SELF)" << std::endl;
        } else {
            unsigned long long dmax= cur_n/k;

            while (dmax%k == 0)
                dmax= dmax/k;
            std::cout << "Largest divisor for " << cur_n << ": " << dmax<< std::endl;
        }
    }
}
Wolfgang
  • 320
  • 3
  • 12
  • 1
    The approach is correct. If `cur` is divisible by `k`, `cur`'s prime factors at least contain one `k`, and you have to eliminate all of them to get a divisor that's no longer divisible by `k`. But the approach might still be slow when testing a billion numbers. – Ralf Kleberhoff May 18 '20 at 11:36
  • This is certainly true, but I stole myself more or less elegantly from the responsibility with the remark: whereby this is certainly also more elegantly possible (since dmax must be a prime number again due to the prime factorization).". – Wolfgang May 18 '20 at 11:41
  • 1
    Perfectly ok with that, leaving some work to the OP with his contest challenge. Maybe we can come back next week to see his progress and if needed, improve on the answer. – Ralf Kleberhoff May 18 '20 at 11:44
  • @RalfKleberhoff it is giving time limit exceed for all the test case. Looking very tough to pass all the test case within timelimit. –  May 18 '20 at 13:09
  • Have a look at @OneLyner's answer. He has the key hints to find a fast solution without iterating over all numbers pp below n. But you'll still have to work out the details - and that's the fun part of these challenges, that I don't want to take away from you. – Ralf Kleberhoff May 18 '20 at 13:14
  • @RalfKleberhoff Is there is any pattern in summation , which we have to look for? I think it is a search operation question. because of testing a huge amount of test cases. Is there is any other way to do it in O (logn ) complexity? –  May 18 '20 at 13:14
0

I wonder if something like this is what One Lyner means.

(Note, this code has two errors in it, which are described in the comments, as well as can be elucidated by One Lyner's new code.)

C++ code:

#include <vector>
#include <iostream>
using namespace std;
#define ll long long int

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    int t;
    cin >> t;

    while (t--) {
        ll n;
        ll k, _k, result;
        vector<ll> powers;
        cin >> n >> k;

        result = n * (n + 1) / 2;
        _k = k;

        while (_k <= n) {
            powers.push_back(_k);
            _k = _k * k;
        }

        for (ll p : powers) {
            ll num_js = n / p;
            result -= num_js * (num_js + 1) / 2 * (p - 1);
            int i = 0;
            while (p * powers[i] <= n) {
                result += powers[i] * (p - 1);
                i = i + 1;
            }
        }

        cout << result << '\n';
    }
}
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
  • sir, please see my above-modified code(2nd one). It is giving TLE for 3 test cases.and how to remove that while loop in my code? –  May 19 '20 at 05:12
  • I don't know javascipt much . So it is tough to understand ur code. –  May 19 '20 at 05:21
  • @sushan with small k, it seems that the while loop in your code can still be O(n). I converted my code to C++. – גלעד ברקן May 19 '20 at 11:15
  • sir, ur code is giving wrong answer for input . take t=1, n=1000000000 and k=97. answer should be 494897959532893312 but your code is giving 494844813310384672 –  May 19 '20 at 11:53
  • @sushan where did you get the result for n=1000000000 and k=97, 494897959532893312, from? – גלעד ברקן May 19 '20 at 12:53
  • sir it is there on that expected output from that coding platform. –  May 19 '20 at 12:58
  • @גלעדברקן No that's not what I mean, my answer is correct, this code is not. – One Lyner May 19 '20 at 14:54
  • @sushan One Lyner provided correct code in their answer now. It also shows two mistakes in my code: the (p - 1) should be the original `p`, which is `k`, not the varying one. Then there is also no need to add back (no need for the while loop). – גלעד ברקן May 19 '20 at 16:22