수 N이 있고 이 수를 k진법으로 표현했을 때 모든 자리의 수가 동일하고 2자리 이상일 때 N을 k의 균일수 라고 정의한다.


f(N)은 N을 균일수로 만드는 모든 k값의 합을 뜻하며 문제에서 묻는것은


 이다.


N의 제한이 1e9이므로 1부터 1e9까지 모두 구하면 당연하게도 시간초과가 난다.



1. 아이디어


하나씩 다 구하는건 앞서 말한 것 처럼 TLE가 뜨기 때문에 다른 방법을 생각해야 한다.


균일수의 성질에 대해서 잘 생각해보자. k진법일 때 1로 이루어진 길이가 L인 수는


k^(L-1) + k^(L-2) + k^(L-3) + k^(L-4) + ....  + k + 1


이다. 만약 이게 1로 이루어진게 아니라 2로 이루어진 거라면?


2 * (k^(L-1) + k^(L-2) + k^(L-3) + k^(L-4) + ....  + k + 1)


가 된다. 저건 k-1로 이루어진 형태까지도 변하지 않는 패턴이다. 이 패턴에서 아이디어를 하나 얻을 수 있다.




라는 배열을 만들어서 arr[i][j] = i진법에서 길이가 j인 1로 이루어진 수를 미리 구해놓는다.


그 다음에는 수 N이 주어졌을 때 


1. 모든 자리가 a로 이루어져 있고 

2. 길이가 n일 때 

3. upperbound ( N/a )를 이분탐색으로 찾는다.


이렇게 하면 시간복잡도 만에 


길이가 n이고 모든 자리가 a인 균일수가 N보다 작거나 같다는 조건 하에 몇진법까지 표현 가능한가를 말해준다.


예를들어 N = 100, a = 1, n = 3일 때 이분탐색을 할 배열 모양새는


{ 7, 13, 21, 31, 43, 57, 73, 91, 111,,,, } 이고


{ 111(2), 111(3), 111(4), 111(5), 111(6), 111(7), 111(8), 111(9), 111(10),,,, }


을 뜻한다. 그러므로 100보다 작거나 같고 3자리면서 111 형태인 균일수는 총 9개이며


이는 a+1 ~ upperbound ( N/a ) 까지의 등차수열 합과 같다.


이대로 짜보자. TLE가 떴다. 그 뒤에 틀렸습니다가 뜬 것도 있으니 시간복잡도상 문제가 되는건지는 잘 모르겠다.


어쨋든 TLE가 떴으니 시간을 줄일만한걸 생각해 보자.


이분탐색을 할 때 2자리 짜리 균일수도 구했는데 이게 생각보다 시간을 많이 먹는 것 같다.


미리 배열을 만들어 놓을 때 [][2], [][3]만 31622까지고 나머지는 엄청 작으니 이걸 줄이면 제법 빨라질 것 같다.


2자리수는 가만보면 단순한 식으로도 계산이 가능했다. 모든 수 N은 N-1진법으로 11(N-1)로 표현이 가능하기 때문이다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>
using namespace std;
 
typedef unsigned long long ull;
 
ull tc;
ull mul[32000][28];
ull idxs[28];
 
void init() {
    for (register ull i = 2; i*<= 1e9++i) {
        ull cnt = 0, pd = i * i*i, temp = i + 1 + i * i;
        while (temp <= 1e9) {
            mul[i][cnt] = temp;
            idxs[cnt] = i;
            cnt++;
            temp += pd;
            pd *= i;
        }
    }
}
 
inline ull run(ull N) {
    register ull ans = 0;
    for (register ull a = 1; a*< N; ++a) {
        if (a + 1 >(N - a) / a) continue;
        ans += (((N - a) / a)*(((N - a) / a) + 1)) / 2 - (a * (a + 1)) / 2;
        for (register ull n = 0; n < 27++n) {
            if (mul[a + 1][n] * a > N) break;
            ull l = a + 1, r = idxs[n], mid, cc = 0;
            while (l <= r) {
                mid = (l + r) / 2;
                if (mul[mid][n] * a <= N) {
                    cc = mid;
                    l = mid + 1;
                }
                else
                    r = mid - 1;
            }
            if (cc > 1)
                ans += (cc * (cc + 1)) / 2 - (a * (a + 1)) / 2;
        }
    }
    return ans;
}
 
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
 
    init();
    cin >> tc;
 
    for (ull t = 1; t <= tc; ++t) {
        ull N, result;
        cin >> N;
        result = run(N);
        cout << "#" << t << " " << result << '\n';
    }
 
    return 0;
}
cs


'SWEA' 카테고리의 다른 글

3752. 가능한 시험 점수  (0) 2018.08.29
5357. 터널 속의 기차  (0) 2018.08.29
5356. 의석이의 세로로 말해요  (0) 2018.08.29
5295. 흘러라 시간! 딴 짓 하기  (0) 2018.08.21
5293. 이진 문자열 복원  (0) 2018.08.21

+ Recent posts