Binary Lifting | Planet Queries 1 | TLE

Viewed 149

I am solving this problem on CSES.

Given n planets, with exactly 1 teleporter on each planet which teleports us to some other planet (possibly the same), we have to solve q queries. Each query is associated with a start planet, x and a number of teleporters to traverse, k. For each query, we need to tell where we would reach after going through k teleporters.

I have attempted this problem using the binary lifting concept.

  1. For each planet, I first saved the planets we would reach by going through 20, 21, 22,... teleporters.

  2. Now, as per the constraints (esp. for k) provided in the question, we need only store the values till 231.

  3. Then, for each query, starting from the start planet, I traverse through the teleporters using the data in the above created array (in 1) to mimic the binary expansion of k, the number of teleporters to traverse.

For example, if k = 5, i.e. (101)2, and the initial planet is x, I first go (001)2 = 1 planet ahead, using the array, let's say to planet y, and then (100)2 = 4 planets ahead. The planet now reached is the required result to the query.

Unfortunately, I am receiving TLE (time limit exceeded) error in the last test case (test 12).

Here's my code for reference:

#define inp(x) ll x; scanf("%lld", &x)

void solve()
{
    // Inputting the values of n, number of planets and q, number of queries.
    inp(n);
    inp(q);

    // Inputting the location of next planet the teleporter on each planet points to, with correction for 0 - based indexing
    vector<int> adj(n);
    for(int i = 0; i < n; i++)
    {
        scanf("%d", &(adj[i]));
        adj[i]--;
    }

    // maxN stores the maximum value till which we need to locate the next reachable plane, based on constraints. 
    // A value of 32 means that we'll only ever need to go at max 2^31 places away from the planet in query.
    int maxN = 32;

    // This array consists of the next planet we can reach from any planet. 
    // Specifically, par[i][j] is the planet we get to, on passing through 2^j teleporters starting from planet i.
    vector<vector<int>> par(n, vector<int>(maxN, -1));
    for(int i = 0; i < n; i++)
    {
        par[i][0] = adj[i];
    }
    for(int i = 1; i < maxN; i++)
    {
        for(int j = 0; j < n; j++)
        {
            ll p1 = par[j][i-1];
            par[j][i] = par[p1][i-1];
        }
    }

    // This task is done for each query.
    for(int i = 0; i < q; i++)
    {
        // x is the initial planet, corrected for 0 - based indexing.
        inp(x);
        x--;
        // k is the number of teleporters to traverse.
        inp(k);

        // cur is the planet we currently are at.
        int cur = x;

        // For every i'th bit in k that is 1, the current planet is moved to the planet we reach to by moving through 2^i teleporters from cur.
        for(int i = 0; (1 << i) <= k ; i++)
        {
            if(k & (1 << i))
            {
                cur = par[cur][i];
            }
        }

        // Once the full binary expansion of k is used up, we are at cur, so (cur + 1) is the result because of the judge's 1 - based indexing.
        cout<<(cur + 1)<<endl;
    }
}

The code gives the correct output in every test case, but undergoes TLE in the final one (the result in the final one is correct too, just a TLE occurs). According to my observation the complexity of the code is O(32 * q + n), which doesn't seem to exceed the 106 bound for linear time code in 1 second.

Are there any hidden costs in the algorithm I may have missed, or some possible optimization?

Any help appreciated!

1 Answers

It looks to me like your code works (after fixing the scanf), but your par map could have 6.4M entries in it, and precalculating all of those might just get you over the 1s time limit.

Here are a few things to try, in order of complexity:

  1. replace par with a single vector<int> and index it like par[i*32+j]. This will remove a lot of double indirections.

  2. Buffer the output in a std::string and write it in one step at the end, in case there's some buffer flushing going on that you don't know about. I don't think so, but it's easy to try.

  3. Starting at each planet, you enter a cycle in <= n steps. In O(n) time, you can precalculate the distance to the terminal cycle and the size of the terminal cycle for all planets. Using this information you can reduce each k to at most 20000, and that means you only need j <= 16.

Related