수열은 재귀적으로 정의되었습니다. 대개 이런 경우 십중팔구 DP로 해결이 가능한데, 수열의 특성을 먼저 파악해 봅시다.
이진수의 길이는 배씩 늘어납니다. 번째 원소의 길이는 이므로 엄청나게 길이가 커질 것을 알 수 있습니다.
치환하는 연산은 의 왼쪽에 을 추가하고, 의 왼쪽에는 을 추가하는 것으로 이해할 수 있습니다. 따라서 가장 첫 자리는 과 을 교대로 반복하고, 마지막 자리는 로 고정됨을 알 수 있습니다.
에서 시작해서 수열을 만들어 나간다면 어떨까요? 원래 수열이 고 에서 시작하는 수열을 이라고 하면 다음과 같은 형태입니다.
인데 여기서 왼쪽에 있는 을 이라고 생각할 수 있습니다. 이렇게 하면 수열을 재귀적으로 다음과 같이 표현할 수 있습니다.
그런데 을 관찰해 보니 또한 이런 방식으로 표현할 수 있습니다.
에 대해서 연속된 들의 그룹의 개수를 각각 함수 로 정의하면 다음과 같습니다.
(가 홀수라면)
의 끝부분과 의 첫 부분은 과 을 교대로 반복합니다. 이 홀수라면 가운데에 맞닿는 부분이 끼리 만나므로 그룹의 개수를 하나 줄여줘야합니다.
는 이므로 의 시간에 구할 수 있습니다. 다시 정리하면 (가 홀수라면) 입니다.
DP배열을 구간 합 배열로 정의하면 의 시간동안 를 구하지 않고 이전 값을 그대로 쓰면 됩니다. 물론 범위가 작기 때문에 으로도 충분히 돌아갑니다.
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
BigInteger[] dp = new BigInteger[N + 1];
dp[0] = new BigInteger("0");
dp[1] = new BigInteger("0");
for (int i = 2; i <= N; i++) {
dp[i] = dp[i - 1].multiply(BigInteger.TWO).add(BigInteger.ONE);
if (i % 2 == 1) dp[i] = dp[i].subtract(BigInteger.ONE);
}
System.out.println(dp[N].subtract(dp[N - 1]));
}
}