[NOIP1999 Popularization Group] Cantor table

[NOIP1999 Popularization Group] Cantor table

Title description:

One of the famous proofs of modern mathematics is Georg Cantor's proof that the rational numbers are enumerable. He proves this proposition with the following table:

1/1 ,   1/2 ,   1/3 ,   1/4,   1/5,   …

2/1,   2/2 ,   2/3,    2/4,    …

3/1 ,   3/2,    3/3,    …

4/1,    4/2,    …

5/1,   …

We number each item in the table above in a zigzag. The first item is 1/1, then 1/2, 2/1, 3/1, 2/2, ...

Input format:

Integer N (1 <= N <= 10^7).

Output format:

Nth item in the table.

Example #1:

Sample input #1:

7

Sample output #1

1/4

Ideas:

Algorithm 1: Simulation

Simulate, enumerate one by one according to the meaning of the topic

Time complexity O(n), you can pass this question n≤10^7.

Algorithm 2: Enumeration

  Each slash that finds the zigzag can be enumerated quickly, that is, enumeration

  1/1 , 1/2 , 3/1 , 1/4 , 5/1 , 1/6... Find the slash where the required nth item is located, and then enumerate or calculate the answer one by one

  Time complexity O(√n), can pass n≤10^14

Algorithm 2.2: Enumeration Simplification

  Enumerate which row the nth item is in, and calculate the answer, which is easier to write than Algorithm 2,

  The time complexity is the same as Algorithm 2

Algorithm 3: Divide

  It is found that the i-th slash (that is, all items where the sum of the numerator and denominator = i+1) contains each item from i*(i-1)/2+1 to i*(i+1), so it can be divided into two The sum of the numerator and denominator, and then directly calculate the nth item according to the parity of the sum of the numerator and denominator

  Time complexity O(㏒₂n), can pass n≤10^18, plus high-precision can pass n≤10^1000

Binary algorithm code:

#include<iostream>
#include<cmath>
using namespace std;
int main(){
    long long l=1,r,mid,n,a;
    cin>>n;
    r=n;
    while(l<r){
        mid=(l+r)/2;
        if(mid*(mid+1)/2<n)
		  l=mid+1;
        else 
		  r=mid;
    }
    a=n-l*(l-1)/2;
    if(l%2==0)
	  cout<<a<<'/'<<l+1-a;
    else 
	  cout<<l+1-a<<'/'<<a;
    return 0;
}

Of course, the simplest thing is to push directly from "mathematics", and the complexity of O(1) takes off directly, so let's push it directly!

Algorithm 4: Mathematical direct push

The known data is the first one.

The triangle drawn in an obvious Z shape, the number of rows from the upper left to the lower right is an arithmetic sequence starting from 1 with a tolerance of 1 . So using the summation formula , if the number of rows is set to be:

Therefore we set such that 

According to the incrementality of the established function , we know that

Therefore, by finding the root formula and then rounding up, the number of rows can be calculated in O(1) time complexity.

Which is 

Next, ask for the specific location of the source in the current line, which is very easy, you only need to know how many there are in that line: the obvious ones

So what is required is the first one of that line .

Next is a Cantor form method for knowing the number of rows + the number :

For all individuals in row a, there are ("/" on the left) + ("/" on the right)

At the same time  ,

The output should be:

<u>(_number_ )/(a+1- _number_ )</u>

<u>The rest of the "/" should be reversed. </u>

The above is O(1) ( in fact, it should not be much faster than two points, which is equivalent to letting the system make three points ) to solve this problem. Since it is a mathematical calculation, the code will not be posted, it is meaningless. 

Summarize:

  The topic of this question is very simple and can be solved with a very simple algorithm, but we can draw inferences from one instance, think more deeply, and think of a more powerful, more efficient and faster algorithm.

Topic Link:
[NOIP1999 Popularization Group] Cantor Table - Luogu https://www.luogu.com.cn/problem/P1014

Guess you like

Origin blog.csdn.net/wo_ai_luo_/article/details/130231288