### fried-chicken's blog

By fried-chicken, history, 6 days ago,

This is a problem from 2021 ICPC Shannxi invitational and confuse me a lot. Since this contest have neither place to up-solving nor tutorial, I'm here to ask for idea and help.

The problem is like:

Given a number X, we call another number N is a lucky number to X if:

1. N is larger than 1e8;
2. N is smaller than 1e13;
3. (N!) begins with X.

For example, if we ignore the first constraint, N=10 is a lucky number to X=3628 since (10!)=3628800.

Now, given T testcases, each testcases gives you a X, output any N where N is a lucky number to X.

T is smaller than 200 and X is smaller than 1e5.

Sample input

2

494

997

Sample output

1000001586369

1000001980150

UPD: Thanks everybody for helping, I think using stirling's and brute force is the right one.

• +13

 » 6 days ago, # | ← Rev. 3 →   +15 Disregarding precision issues, using the Stirling series approximation to the second term, we have $N! \approx \sqrt{2 \pi N} \left(\frac{N}{e}\right)^N \cdot \left(1 + \frac{1}{12N}\right)$with a relative error of $O\left(\frac{1}{N^2}\right)$, which should suffice to find the first $10^5$ digits (if you take logarithms).Implementing this might be pretty painful though (if precision issues persist, Newton Raphson to do exponentiation for bigints should do the trick).Edit: I misread the problem, the solution here might not be helpful in solving the original problem.
 » 6 days ago, # |   0 Maybe brute force?If the distribution is random, it may be possible to store only the highest few post-violence.And, Preprocessing $10^8!$.
 » 6 days ago, # | ← Rev. 12 →   0 Check if the following Stirling's approximation solution is accurate enough to work or not. C++#include using namespace std; using ld = long double; inline ld Stirling_approximation(int N) { using ll = long long; using lvec = vector; using lmat = vector; static const ld one = 1, two = 2, ten = 10; static const ld b = log10(two*acos(-one))/two, e = exp(one); static const lmat c = {{1,1},{1,12},{1,288},{-139,51840}, {-571,2488320},{163879,209018880},{5246819,75246796800ll}}; ld f = b+log10(N)/two+N*log10(N/e), g = 0, p = one; for (int i = 0; i < 7; ++i, p /= N) g += (p*c[i][0])/c[i][1]; f += log10(g), f = pow(ten,f-ll(f)); assert(f > one and f < ten); return f; } int main() { const int P = 1e5; vector ans(P,-1); int N = 1e8+1; ld f; for (int X, i, d, found = 1; found < P; ++N) for (f = Stirling_approximation(N), X = 0, i = 0; i < 5; ++i) if (d = f, f -= d, f *= 10, X *= 10, X += d, ans[X] == -1) ans[X] = N, ++found; cerr << "all " << P-1 << " prefixes were found with N < " << N << endl; int T; cin.tie(nullptr)->sync_with_stdio(false), cin >> T; for (int X; T--; cout << ans[X] << '\n') cin >> X; return 0; } Since $1 \leq X < 10^5$, it should be sufficient to check if the $k$-digit prefix of $N!$, where $1 \leq k \leq 5$, is equal to $X$. This program reported the following prompt to cerr. promptall 99999 prefixes were found with N < 102184933 2,184,933 preprocessing iterations of Stirling's approximation computation were sufficient to find the answers to all possible values of $X$. In Codeforces C++20 custom test, the preprocessing part consumed 624 ms execution time.
 » 6 days ago, # | ← Rev. 2 →   0 I made a brute force, and it seems to work. I just copied the value of (10^8)! from Wolfram Alpha. In the brute force I try every factorial from 1e8+1 onwards until I find the right prefix, (it only takes around 10^6 tries in the worst case to find the right prefix) . To make the numbers fit in a long double, I divide by 10 when needed. Then it's just hoping there are no precision errors :)Edit: I checked what the largest needed value of n was. In my program it is 102184933. c++#include "bits/stdc++.h" using namespace std; #define all(x) begin(x),end(x) typedef long long ll; const int START = 1e8; int pw=1; long double mul(long double b, ll a) { b*=a; while(b>=pw) b*=0.1; return b; } void solve(int x) { long double res=1.617203794921462386338e10; ll ans = START; pw=1; while(pw<=x) pw*=10; res = mul(res,1); while(true) { if(ans!=START and floor(res)==x and (res-floor(res))>1e-10) { cout << ans << '\n'; break; } res = mul(res,++ans); } } int main() { int x; cin >> x; solve(x); } 
 » 6 days ago, # |   0 Auto comment: topic has been updated by fried-chicken (previous revision, new revision, compare).
 » 5 days ago, # | ← Rev. 4 →   0 I have just checked out of curiosity the maximum absolute difference between approximating $N!$ as $f\times 10^g$, where $1 \leq f < 10$ and $g$ is non-negative integer using Stirling's approximation, and using the power function of logarithmic summation. $N! = 10^{\sum\limits_{x=1}^{x=N} log_{10}(x)}$ I found that the maximum absolute difference between the two approximations is 0.0000031938, which is less than $10^{-5}$. The execution-time for the logarithmic summation in Codeforces custom test is about 3500 ms. It seems reasonable to claim that Stirling's approximation is fast and accurate enough to provide the correct answer for at most 5-digit prefix of $N!$ of the given constraints $1 \leq X < 10^5$ and $N > 10^8$.