3

Card Trick is a problem on Sphere online judge.
It states that

The magician shuffles a small pack of cards, holds it face down and performs the following procedure:

  1. The top card is moved to the bottom of the pack. The new top card is dealt face up onto the table. It is the Ace of Spades.

  2. Two cards are moved one at a time from the top to the bottom. The next card is dealt face up onto the table. It is the Two of Spades.

  3. Three cards are moved one at a time…

  4. This goes on until the nth and last card turns out to be the n of Spades.

This impressive trick works if the magician knows how to arrange the cards beforehand (and knows how to give a false shuffle). Your program has to determine the initial order of the cards for a given number of cards, 1 ≤ n ≤ 20000.

Input

On the first line of the input is a single positive integer, telling the number of test cases to follow. Each case consists of one line containing the integer n.

Output

For each test case, output a line with the correct permutation of the values 1 to n, space separated. The first number showing the top card of the pack, etc…

Example

Input:
2
4
5

Output:
2 1 4 3
3 1 4 5 2

Now the only solution I can think of is to use a queue and simulate the process.
But that would be O(n^2). I read the comments and they suggested using segment tree of BIT.
I know both segment tree and BIT but am unable to understand how to implement them in this question.
Please suggest some way to do this.

greybeard
  • 2,249
  • 8
  • 30
  • 66

3 Answers3

4

I have no idea why this problem should be linked with BIT or segment tree, but I solved the problem using simple "O(N^2)" simulation.

First the time limit for this problem is 11s, and N == 20000. This indicates that a O(kN) solution may pass the problem. I believe you think this k should be N, because simple simulation requires this, but somehow it can be optimized.

Let's see how we can construct the sequence when N == 5:

Round 1, count 1 space starting from first space after last position:        _ 1 _ _ _
Round 2, count 2 spaces starting from first space after last position:       _ 1 _ _ 2
Round 3, count 3 spaces starting from first space after last position:       3 1 _ _ 2
Round 4, count 4 spaces starting from first space after last position:       3 1 4 _ 2
Round 5, count 5 spaces starting from first space after last position:       3 1 4 5 2

We can see a nice pattern: for round i, we should count i space starting from the first space after last position, and warp back when necessary.

However, a crucial step is: after some rounds, the spaces left will be smaller than the space to count. In this case, we can take a mod to save time!

For example, in Round 4 of the previous example, we have only 2 spaces left but 4 spaces to count. If we count 4, it's a waste of time. Counting 4 steps is equivalent to count 4 % 2 == 0 space starting from the first space after last position. You can verify this point by yourself :)

Therefore, we can simulate this process using the code:

memset(ans, 255, sizeof(ans));
while (cur <= n)
{
    int i, cnt;
    int left = n - cur + 1; // count how many spaces left
    left = cur % left + 1; // this line is critical, mod to save time!

    for (i = pos, cnt = 0; ; ++i) // simulate the process
    {
        if (i > n) i = 1;
        if (ans[i] == -1) ++cnt;
        if (cnt == left) break;
    }

    ans[i] = cur;
    pos = i;

    ++cur;
}
nevets
  • 4,631
  • 24
  • 40
3

If you want to use a Fenwick tree (BIT) to solve this problem, take a closer look at the solution that nevets posted, particularly this part (thanks for the drawing nevets):

Round 1, count 1 space  starting from first space after last position:       _ 1 _ _ _
Round 2, count 2 spaces starting from first space after last position:       _ 1 _ _ 2
Round 3, count 3 spaces starting from first space after last position:       3 1 _ _ 2
Round 4, count 4 spaces starting from first space after last position:       3 1 4 _ 2
Round 5, count 5 spaces starting from first space after last position:       3 1 4 5 2

Finding the correct free space using the above approach has a time complexity of O(N) because we have to go thru all the spaces (total complexity O(N^2)). Notice that we can calculate the next position using:

free(next_pos) = (free(current_pos) + next_number) mod free(total) + 1

where free(x) tells us how many free spaces are up to (including) a position. This is not a direct formula for next_pos, but it tells us what it needs to satisfy, so we can use this information to binary search it.

The only thing left to do is to do the free space calculations, and this is where BIT comes into play as it gives us a time complexity of O(log N) for both query and for update. The time complexity of finding a free space is now O(log^2 N) and the total time complexity is O(N log^2 N).

As for the running speed:

  • 3.16s for the approach nevets suggested
  • 1.18s using a queue to rotate the elements
  • 0.60s using a linked list to rotate
  • 0.02s using a BIT.

I must say I was quite surprised by the speed gain :-)

P.S. If you're not sure how to use the BIT, initialise by updating all values by +1. When marking a slot as taken, just update it by -1, that's it.

Ivan Š
  • 1,024
  • 2
  • 10
  • 15
0

We can solve this also using an indexed set (normal set but with the ability to reach elements in it by indexes like the arrays and vectors)

We can consider this as a faster implementation for the approach of @nevets and time complexity will be O(NlogN).

Instead of looping throw all elements to find the correct free space. We will store all the free spaces in the indexed set. and every time we take a free space we erase it from the set. and every time we want to find new correct free space we can find it in O(1)

typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> indexed_set_int;

int main() {
    int n;
    cin >> n;
    int a[n]

    indexed_set_int st;
    for(int i = 0; i < n; i++) {
        st.insert(i);
    }

    int ind = 0;
    for(int i = 0; i < n; i++) {
        ind += i+1;
        ind %= st.size();  // We should mod it to the size of the set to avoid going outside the boundry of the set
        auto it = st.find_by_order(ind);  // This will get the index of the correct posision from the set of free poisions in O(N)
        a[*it] = i+1;
        st.erase(it);  // remove the free space from the set because we already used it.
    }
    for(int i = 0; i < n; i++) {
        cout << a[i] << " ";
    }
}

This solution will be at least the same fast as the BIT. I didn't compare the actual speed of both approaches but from the Time complexity, they both are fast.

Omar_Hafez
  • 272
  • 2
  • 13