ASYMTILING (중, p. 259)

소요 시간: 30분

책 난이도는 ‘하’였는데, ‘중하’도 아니고 ‘중’수준인 듯 하다.

점화식 자체는 데칼코마니같은 경우를 빼지 않을 때를 먼저 구하고,

여기서 데칼코마니 경우를 빼주면 된다.

여기서 처음에는 그래서 x칸의 타일을 채우는 방법은 f(x) - f(x/2)라고 생각했으나,

x가 짝수일 땐 f(x)-f((x-1)/2)-f(x/2)가 된다.

잘 생각해보면,

단순히 f(x)-f(x/2)라 하면, 짝수일 때 중앙에 블록이 2x1짜리가 왔을 때 ………||……… ← 이런 형태가 된다.

근데 이 점화식으론 ………=……… ← 이런 경우의 수를 셀 수 없다.

즉, f(x)에서 x가 짝수일 때, 아래 2가지 경우를 빼줘야 한다.

………||……… → f(x/2)

………=……… → f((x-1)/2)

이러한 점화식을 생각해내는게 어렵진 않지만, 위의 -f(x/2)-f((x-1)/2)부분을 -2*f(x/2)로 간과하기 쉬운 것 같다.

여기서 헤매기 시작하면 문제푸는데 상당히 오래걸리고, 모듈러 연산할 때 (x-y)%M == (x-y+M)%M인 것도 알아야 하기때문에 난이도 ‘하’라고 하기엔 무리가 있다.

난이도 ‘중하’로 하려했으나 위처럼 고려해야 할 부분이 여러 개 있고, 생각보다 오랜 시간이 걸릴 수 있을 것 같아 ‘중’으로 조정했다.

image.png

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M = 1e9+7;
bool fchk[101], gchk[101];
ll fdp[101], gdp[101];
ll f(int x) {
    if(x<=1) return 1;
    if(fchk[x]) return fdp[x];
    fchk[x] = true;

    return fdp[x] = (f(x-1)+f(x-2))%M;
}
ll g(int x) {
    if(x<=1) return x^1;
    if(gchk[x]) return gdp[x];
    gchk[x] = true;

    ll & ret = gdp[x];
    if(x%2==0) ret = (f(x)-f((x-1)/2)-f(x/2)+2*M)%M;
    else ret = (f(x)-f(x/2)+M)%M;
    return ret;
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    int tc; cin >> tc;
    while(tc--) {
        int n; cin >> n;
        printf("%lld\n", g(n));
    }
    return 0;
}