지름목록 1 WD My Passport Ultra 2TB

2테라 14만원으로 다른 외장하드가 17만원 정도인 것에 비해 상당히 싸다. 얘를 만난 첫인상은 ‘무겁다’였다. 기존에 쓰던 외장하드에 비해 무게 차이가 느껴질 정도로 묵직했다. 크기는 좀 작은 대신 두께가 좀 두껍다. 처음에는 가격도 싸고 정품 스티커 같은것도 없어서(사실 원래 주는지를 모름) 혹시 사기가 아닐까 했는데 컴퓨터 연결해보니 WD 소프트웨어 업데이트 적용이 되는걸로 봐서 아닌 것 같다.

기존 1테라 하드를 거의 다 썼었는데 이번 지름으로 인해 여유공간이 상당히 늘어났다. 앞으로 신세를 많이 지게 될 듯.

지름목록 2 신 세계수의 미궁 밀레니엄의 소녀

3DS 게임 좀 추천해달라는 글에 대부분 젤다의 전설 신들의 트라이포스 2를 추천해주었고, 나도 그럴 예정이었는데… 이게 초회판 특전이 있었다.

초회판은 OST CD를 끼워 주는데, 이게 나온지 좀 된 게임이라 다 떨어졌을거라 생각했는데 쇼핑몰 사이트를 잘 뒤지다보니 품절되지 않은 곳을 찾을 수 있었다. 그래서 언제든 살 수 있는 젤다보다는 다 떨어져가는 초회판은 구입해야 한다는 충실한 덕후본능을 따라 세계수의 미궁을 질렀다.

게임 자체는 일반적인 던전 RPG인데 특이한 점은 지도를 직접 그려야 한다.(던전은 랜덤 생성 아님) 자동 그리기를 켜더라도 자기가 서 있는 칸의 바닥 / 벽이 그려지는게 다라서 약간 불편하지만 그 불편함에 매력이 있다.

지도에 다양한 아이콘들과 메모를 배치할 수 있는데, 이쪽에는 지름길이 있다던지 여기서는 회복 이벤트가 발생한다던지 이런걸 자신만의 스타일로 전부 기록해 놓을 수 있다. 지도에 경로를 그려 놓으면 그 경로를 따라서 자동 이동하는 기능도 있다. 맵을 직접 그리는 것을 통해 판타지의 묘미인 ‘미지의 세계를 탐험’한다는 느낌을 잘 살렸다고 생각한다.

난이도를 고르라길래 낮은 난이도로 했다가 나중에 다시 높은 난이도로 깨기 귀찮아서 그냥 최고 난이도로 플레이 중인데 너무 어렵다. 필드몹에 한 대 맞으면 1/3 정도가 깎이고, 필드보스(FOE)라도 만나는 날에는 애들이 낙엽 떨어지듯 썰려 나간다. 그래도 밸런스를 절묘하게 맞춰서 도전하는 재미가 있음.

선발고사 때도 그랬듯이 당일날보다 그 다음날 후유증이 더 큰 듯 ㅠㅠ

어제 KOI 3번을 대회장에서 못 푼게 아쉬워서 같은 유형의 문제인 APIO Sequence를 짜 봤다. 몇 번 오답을 제출하기는 했는데 전부 다 0 원소 처리 제대로 안 해서 그랬으니까 모든 원소가 양수였던 대회장에서 짰던 CHT는 맞았을 것이고, 결국 점화식 부분에서 계산실수만 있었던 것 같다.

수학에서는 전체적인 과정을 찾아내면 단순한 계산 실수가 있어도 대부분의 점수를 받는데 정올에서는 과정을 다 알아도 단순한 계산 실수가 있으면 0점이다. 학문간의 성격이나 특징에 차이가 있고 참가자한테 일일이 코드가 무슨 의미를 가지는지 물어볼 수도 없으니 이해는 하지만 참 아쉬운 점이라고 생각한다.

어제 KOI를 마지막으로 대학생 전에 참가할 수 있는 프로그래밍 대회는 전부 끝났다. 경기과학고등학교에 입학할 당시에만 해도 국가대표나 KOI 금상 이상 둘 중 하나는 당연히 할 수 있을거라 생각했는데 KOI 은상 세 개라는 평범한 성적으로 고등학교 학창시절을 마감하게 되었다.

선발고사도 그렇고 KOI도 그렇고 지금 느끼는 아쉬움 중 가장 큰 부분은 내 실력만큼의 성과를 거두지 못했다는 점이다. 내가 풀 수 있는 문제를 다 풀고서 받은 결과가 은상이라면 결과에 만족했겠지만, 그렇지 않아서 안타깝다. 남들한테는 잘난척이나 부심이라고 보일 수도 있겠지만, 작년도 그렇고 이번도 그렇고 충분히 금상 이상의 성적을 거둘 수 있는 실력이었는데도 은상을 받았다.

뭐 남들한테 인정 받으려고 공부하는 것도 아니고, 상을 못 받았다고 해서 있던 내 지식이 없어지는 건 아니지만 역시 아쉽기는 하다. 선발고사도 1차 때 잘 봤는데 2차 때 망하고, APIO도 1점 차이로 세계 동탑을 받았고, 작년이랑 이번 KOI는 문제 다 풀어놓고 사소한 실수를 못 찾아서 은상을 받았다. 전부 한 걸음을 남기고 안타깝게 실패한 경험들이었다.

내 인생은 아직 반도 지나지 않았고 아직 기회들은 많이 남아 있다. 그리고 꾸준히 공부하면서 실력을 갈고 닦는다면 남아 있는 기회들 중 내가 잡을 수 있는 기회가 언젠가는 찾아올 것이다. 하지만 그것은 어른이 된 뒤의 경험이고, 학창시절의 경험과는 다른 의미를 지닌다고 생각한다. 그렇기 때문에 정말 마지막이었던 이번 결과에 평소보다 더 미련이 남는 것 같다.

지금까지 그래왔듯이 며칠 지나면 금방 또 셀프디스하고 웃고 떠드는 추억이 되겠지만, 아직까지는 안타까운 마음이 큰 것 같다. 이제 진짜 다 끝났으니까 입시공부나 해야지 헤헤.

최종적으로 200점을 받았다. 처음 목표가 2, 3번을 풀고 1번을 긁는 것이었는데 목표는 달성해서 만족스럽다.

처음에 3번을 잡았는데, 아무리 생각해도 인덱스 트리로 log N번 연산하는 방법밖에 떠오르지 않아서 말렸다. 다른 방법을 생각하려고 해도 너무 졸려서 아무런 생각이 안 나서 잠깐 자고 일어났다.

다행히 자고 일어나니까 컨디션이 괜찮아져서 다시 풀기 시작했다. 3번을 좀 더 잡고 있다가 2번으로 갈아탔다. 2번은 풀릴 것 같은 풀이가 금방 생각나기는 했는데 예외 케이스가 있을 것 같다는 생각 때문에 풀이 검증에 시간이 걸렸다. 의사 코드로 표현하면 다음과 같고, 실제로는 리넘버링 및 인덱스 트리를 써서 구현했다.

Pinball
fill L[0 .. N] with INF
fill R[0 .. N] with INF

L[0] = 0
R[N] = 0

let res = INF

for each machine {
    let costL = min L[machine.L .. machine.R]
    let costR = min R[machine.L .. machine.R]

    res = min(res, costL + costR + machine.D)

    L[machine.C] = min(L[machine.C], costL + machine.D)
    R[machine.C] = min(R[machine.C], costR + machine.D)
}

print res == INF ? -1 : res

3번은 생각을 뒤집어서, ‘이런 연산 결과를 어떻게 구할 수 있을까’가 아니라 ‘쿼리에서 한 번만 연산을 할 수 있다고 할 때 어떻게 할 것인가’로 생각을 바꾸었더니 금방 떠올랐다. 기준점들을 잡아서 기준점 좌우의 연산값을 저장해 놓은 뒤, 쿼리가 들어오면 쿼리 구간에 포함되는 기준점을 찾아 저장된 값을 이용해 합치면 되겠다는 생각을 했다.

이제 이러한 기준점들을 어떻게 잡아야 효율적으로 Init 함수에서의 쿼리 횟수를 줄일 수 있을 것인지를 생각하면 되는데, 이진 탐색의 중심점들을 이용하면 된다. 코드는 다음과 같다.

Secret
#include "secret.h"

#include <vector>

using namespace std;
const static int MAX = 1010;

static int n;
static vector < int > left[MAX], right[MAX];

void cut(int l, int r, int A[]){
    if(l > r) return;

    int m = (l+r)>>1;

    int i, last;

    last = A[m+1];
    right[m].push_back(A[m+1]);
    for(i = m+2; i<=r; i++){
        last = Secret(last, A[i]);
        right[m].push_back(last);
    }

    last = A[m];
    left[m].push_back(A[m]);
    for(i = m-1; i>=l; i--){
        last = Secret(A[i], last);
        left[m].push_back(last);
    }

    cut(l, m-1, A);
    cut(m+2, r, A);
}

void Init(int N, int A[]){
    n = N;
    cut(0, N-1, A);
}

int Query(int L, int R){
    int nowL = 0, nowR = n-1;
    while(true){
        int m = (nowL+nowR)>>1;
        if(R == m){
            return left[m][R-L];
        } else if(L == m+1){
            return right[m][R-L];
        } else if(L <= m && m+1 <= R){
            return Secret(left[m][m-L], right[m][R-m-1]);
        } else {
            if(m+1 < L) nowL = m+2;
            else if(R < m) nowR = m-1;
        }
    }
}

2, 3번을 다 풀고 나니 30분쯤 남았는데 1번이 기하에 output only라 제대로 건드려보지 못하고 끝났다. 그래도 나쁘지는 않았다고 생각한다.

ps. Round 1 2위는 하츠네 미쿠(8tusne 39)였다. 나는 이 분의 정체를 안다 ㅋㅋㅋ

JOI 미쿠

최근 Risk of Rain이라는 게임을 열심히 하고 있다. 한 문장으로 표현하자면 플랫포머 슈팅 서바이벌 게임이다.

지금까지 두 번 클리어 했다. Commando 플레이 할 때는 넉백이랑 빙결 등 상태 이상 아이템이 많이 나와서 밀면서 안정적으로 플레이하면서 몹이 추가 골드를 드랍하는 아이템을 통해 부유한 플레이를 하면서 터렛을 많이 쌓았다. Sniper로 플레이 할 때는 KIN 아티팩트를 켜고 했는데, 모든 공격이 폭발 공격이 되는 Brilliant Behemoth와 액티브 시계가 키 아이템이었다.

한 판 할 때 시간이 너무 오래 걸리는 게 단점이지만 취향에 맞고 재밌다.