For a given number x, we can check whether it is valid or not (i.e. whether chain rounding gives a different result than directly rounding) by directly simulating the chain rounding process. This can be done in O(logN) time.
//round u to the nearest d int round (int u, int d) { if (u % d < d/2) { return u - u % d; } return u + d - u % d; } //chain round u to the nearest target int chain_round(int u, int target){ for(int ten = 10; ten<=target; ten *= 10){ u = round(u, ten); } return u; }
For the N≤1000 subtask, we can check each x from 2 to N using brute force. We will loop over each number and check if it chain rounds to a different number than directly rounding. The total complexity is O(NlogN) per test case.
For the N≤106 subtask, we note that if there are V valid x from 2 to N−1, there are either V or V+1 valid numbers from 2 to N, depending on whether N itself is valid. This motivates us to precompute for each N the number of valid x before processing any of the test cases and storing it into an array. This lowers our time complexity to O(1) per test case! We still need O(NlogN) time to precompute all possible answers, so our final time complexity is O(NlogN+T) where T is the number of test cases.
#include <iostream> #include <vector> // round u to the nearest d int round (int u, int d) { if (u % d < d/2) { return u - u % d; } return u + d - u % d; } //chain round u to the nearest target int chain_round(int u, int target){ for(int ten = 10; ten<=target; ten *= 10){ u = round(u, ten); } return u; } int main () { const int maxN = 1'000'000; std::vector<int> ans(maxN+1); ans[1] = 0; for(int N = 2; N <= maxN; N++){ int V = ans[N-1]; int target = 1; while(target < N){ target *= 10; } if(chain_round(N, target) != round(N, target)){ V++; } ans[N] = V; } int T; std::cin >> T; while (T--) { int N; std::cin >> N; std::cout << ans[N] << "\n"; } }
For N≤109, it is now too slow to check for each x whether or not it is valid, so we have to make some observations about chain rounding.
With some experimentation, we can observe that all valid x have a prefix of at least one 4 (prefix starting from the left), and the first digit not equal to a 4 must be 5, 6, 7, 8, or 9.
For instance, 4451, 450, and 45 will be valid but 4, 50, and 7455 are not.
If we fix the length of x to be L, then x must be in the range [L−1⏞44…45,4L−1⏞99…9]. We can loop over all possible L (as N≤109, the maximum L is at most 9) and check for each one, how many valid x of length L are less than or equal to N.
Complexity O(logN) per test case.
Anand John's Python code follows. Note that the time complexity is worse than O(logN) due to conversions from strings to integers, but it is still fast enough to pass.
def alg(N): digits = 0 while 10**digits < N: digits += 1 answer = 0 for curdigits in range(1, digits+1): upper = int('5'+'0'*(curdigits-1))-1 upper = min(N, upper) lower = int('4'*curdigits) answer += max(0, upper - lower) return answer T = int(input().strip()) for _ in range(T): N = int(input().strip()) print(alg(N))
Brandon Wang's C++ code:
#include <iostream> #include <vector> //determines the length of the intersection of [a, b] with [c, d] int isect(int a, int b, int c, int d) { int lb = std::max(a, c); int ub = std::min(b, d); return std::max(0, ub - lb + 1); } int solve(int n) { int is = 5, ie = 49; int tp = 1; int ans = 0; while (tp < 100000000) { tp *= 10; is += 4 * tp; ie = 5 * tp - 1; ans += isect(is, ie, 2, n); } return ans; } int main() { int T; std::cin >> T; while (T--) { int n; std::cin >> n; std::cout << solve(n) << "\n"; } }