본문으로 바로가기

Codeforces, 백준 프로그래밍을 위한 Vim 세팅

category Vim 2020. 2. 3. 20:21

글을 시작하기 앞서 나는 프로그래밍 초보임을 명시하겠다.

코드포스 대회 경험도 얼마 없으며 이제 막 오렌지를 단 신생아이다.

 

실제로 내가 이 세팅을 백준이나 코드포스 문제를 푸는데 제법 효율적으로 사용하고 있다는 느낌도 들어서.. 포스팅 한다.

 

Vim이 코딩할 때 도움이 되는가?

Vim을 제대로 사용하면 분명 코딩에 도움이 된다.

 

동영상에 나온 것은 간단한 문제에 대한 코드다. $N$개의 정수가 들어오고, $Q$개의 쿼리가 주어진다. 각 쿼리마다 자연수 $k_i$가 입력으로 주어지며, $k_i$번째로 큰 정수를 출력하고 그 값을 원래 집합에서 지운다.

 

그냥 쌩 C++로 코딩한다면 어떨까. STL에서 여러 가지 이진탐색트리는 지원해주나(map, multiset 등) k번째 원소에 대한 연산을 코딩하려면 머리가 질끈 아파진다. 그런걸 보면 나는 아직 PS 초보가 맞는 것 같다.

 

하지만 라이브러리를 이용하여 40초 안에 문제를 해결하였다. 라이브러리 파일을 직접 열지도 않고, 마우스로 필요한 영역을 복사-붙여넣기하는 일도 없이 키보드 단축키만으로 잘 구현된 treap 이진탐색트리 자료구조를 불러왔다. 코드도 자동으로 이쁘게 코드 접기(folding)가 돼서 줄 수를 잡아먹지도 않는다.

 

더욱 놀라운 것은 이러한 동작 하나하나들을 직접 제작하고 매크로로 사용할 수 있다는 것이다!

 

 

도대체 Vim이 무엇이길래 이런 게 가능한가?

Vim, Emacs 등은 타이핑 그 자체를 자동화할 수 있는 편집기이다. 타이핑 동작 자체를 프로그래밍할 수 있다는 것이다.

그 중 Vim은 굉장히 간단하고 직관적이다. 생각한대로 스크립팅하면 웬만하면 잘 돌아간다.

 

예전에는 플러그인에 의존을 많이 했다. 하지만 여러 가지 충돌 문제, 버전 호환 문제(당장 YouCompleteMe는 C++의 auto 같은 문법을 지원 안 한다.), 원하는 세팅을 하기 힘들다는 문제 등으로 골머리를 많이 겪다보니까 최소한으로 유지하게 되는 것 같다. 차라리 Vim Script를 익혀 원하는 세팅을 직접 만드는게 더 나은 것 같았다.

 

제일 큰 장점은 Vim Script 내에서 Python 코드를 사용할 수 있다는 것이다! 덕분에 Vim Script 문법을 제대로 몰라도 당장 필요한 기능들을 다 파이썬으로 구현하면 원하는대로 잘 돌아간다. 실제로 문자열을 처리하는 기능은 파이썬이 Vim Script보다 1000배쯤 나은 것 같다.

 

 

내가 사용하는 Vim 설정

https://github.com/lego0901/CP_Library

 

lego0901/CP_Library

C++ library for competitive programming. Contribute to lego0901/CP_Library development by creating an account on GitHub.

github.com

영어 고자라 걱정했는데 이 블로그 글보다 더 깔끔하게 정리한 것 같다. 영어가 더 편하다면 저 글을 보자.

 

내가 사용하는 개발 환경은 우분투 18.04 LTS - Vim 8.0 이다. 아마 OS X이나 윈도우의 Ubuntu Bash를 사용하는 분들은 똑같이 설정할 수 있을 것으로 생각한다. 컴파일 방식은 g++을 사용하고 STD C++ 17년도 버전을 주로 사용한다.

 

우선 Vim 설정 파일을 처리하는 파일 형식에 따라 4개로 나누었다. 모두 홈 폴더 내에 있다.

 

  • .vimrc: 공통 설정. set number 같은 것이 들어있다.
  • .cvimrc: C, C++을 사용하기위한 설정. 대회에 사용하는 설정들이 사실 여기에 다 있다고 볼 수 있다.
  • .pythonvimrc: Python을 사용하기 위한 설정. 잘 안 써서 거의 뭐가 없다.
  • .latexrc: LaTeX을 사용하기 위한 설정. map 구성이 난무하다. (여기에 대한 글은 또 나중에..)

그 중에서도 특히 .vimrc와 .cvimrc를 집중적으로 파보도록 하겠다.

 

.vimrc 파일 내용 및 설명

전체 코드는 다음과 같다.

"**************** Common Settings ****************
set encoding=utf-8

set number autoindent smartindent hlsearch "ignorecase
set tabstop=4
set shiftwidth=4
set foldmethod=marker

syntax on

" 마우스 쓸 수 있음! 대회에서 뚝배기 깨지다보면 무조건 쓰게 됨 ㅎㅎ
set mouse+=an
set ttymouse=xterm2

set laststatus=2
set backspace=indent,eol,start
set autoread

if has("autocmd")
  au BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
endif

" 컨트롤 A, C, V (전체선택, 클립보드 복사, 붙여넣기)
nnoremap <C-a> ggVG
inoremap <C-a> <ESC><C-a>
vnoremap <C-c> "+y
nnoremap <C-v> "+p
vnoremap <C-v> d"+p
inoremap <C-v> <ESC><C-v>

" 화면 스크롤 단축키
map <C-Up> <C-w>k
map <C-Down> <C-w>j
map <C-Left> <C-w>h
map <C-Right> <C-w>l



"************** Plugins Installation **************
" 플러그인 안 쓰실거면 이거 다 지우시면 됩니다.
set nocompatible
filetype off

set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()

	Plugin 'VundleVim/Vundle.vim'
	Plugin 'tpope/vim-fugitive'
	Plugin 'L9'
	Plugin 'The-NERD-tree'
	"Plugin 'a.vim'
	"Plugin 'c.vim'
	"Plugin 'Valloric/YouCompleteMe'

call vundle#end()
filetype plugin indent on


"************ NERDTree Plugin Settings ************
" 정작 CP에는 잘 안 쓰는 너드트리
imap <F2> <esc>:NERDTreeToggle<CR>
nmap <F2> <esc>:NERDTreeToggle<CR>

let NERDTreeIgnore = ['\.aux', 'c_out', '\.class', '\.fls', '\.log', '\.fdb_latexmk']
let g:nerdtree_plugin_open_cmd = 'gnome-open'


"***************** My Extensions *****************
au BufRead *.latexrc set filetype=vim
au BufRead *.cvimrc set filetype=vim
au BufRead *.pythonvimrc set filetype=vim

source ~/.cvimrc
"source ~/.latexrc
"source ~/.pythonvimrc


"****************** CUI Options ******************
" 테마/스킨. 마음대로 설정하세요
set background=light
hi Folded ctermbg=8

 

vundle 설치했기 때문에 플러그인 부분이 있다.

플러그인 없이 Pure Vim 만 쓰겠다하면 set nocompatible .... filetype plugin indent on 부분까지 다 지우면 된다.

 

크게 다른 블로그 글이랑 벗어난 것이 많이 없다.

다만, source ~/.cimvrc 는 꼭 넣어주자. 그게 이번 게시물의 메인이기 때문이다.

 

.cvimrc 파일 내용 및 설명

전체 코드를 올린다. .vimrc와 마찬가지로 홈 디렉토리에 저장하면 된다.

 

사용하기 전에 미리 내가 코딩하는 방식을 간단히 설명하면

 

  • 모든 코드는 ~/c 폴더 내에 있다. 보통 ~/c/hello.cpp 파일로 문제를 푼다.
  • 실행 코드는 ~/c/input.txt 파일을 입력으로 하여 ~/c/output.txt 파일에 출력하도록 한다. 콘솔에 바로 입/출력하는 일은 많이 없다. 즉, 코드를 실행할 때 ~/c/make < ~/c/input.txt > ~/c/output.txt 콘솔 명령어를 사용한다.
  • 라이브러리들은 ~/c/library 폴더 내에 저장되어 있다. 예를 들어 treap에 대한 라이브러리는 ~/c/library/treap.cpp 파일에 담겨있다.
  • 새로운 문제를 풀 때 typedef long long ll; 등이 처리되어있는 base 파일을 불러온다. 그 파일은 ~/c/library/base.cpp 이다.

만약 다른 디렉토리나 파일 명을 사용한다면 그에 맞게 코드를 적당히 수정해주면 된다.

 

여기에 대한 주요 단축키 및 매크로는 다음과 같다.

 

  • 베이스 파일을 불러오고자하면 normal 모드에서 :Base 를 입력하고 엔터를 누른다.
  • 코드를 컴파일하고자하면 F5키를, ~/c/input.txt 파일에서 입력을 받아 ~/c/output.txt 파일로 출력하고 싶으면 F6키를 누른다. 콘솔에서 바로 입/출력을 넣고 확인하고 싶으면 F7키를 누른다.
  • 컨트롤 P를 누르면 오른쪽에 ~/c/input.txt, ~/c/output.txt 파일이 뜬다.(동영상 참고) 컨트롤 O를 누르면 지금 포커싱된 창을 제외하고 모두 종료한다. 즉, 컨트롤 P를 눌러 입/출력 파일을 열고 직접 편집하면 된다.
  • 작성한 라이브러리 목록을 보고 싶으면 역슬래시 L을 누르면 된다.
  • 특정한 라이브러리 내용을 불러오고 싶으면 전역변수를 선언하는 곳에 라이브러리 파일 명을 입력한다. 그 단어에 초점을 두고 역슬래시 L을 누르면 불러온다. 예를 들어, ~/c/library/treap.cpp 내용을 불러오고자 한다면, 전역변수를 선언하는 곳에(즉 int main 위쪽에) treap이라고 입력하고 역슬래시 L을 누르면 된다. (동영상 참고)
    • 해당 라이브러리 파일들은 특정한 양식을 가지고 있어야 한다. 이것도 아래에서 설명하겠다.

그 외에도 다른 기능들이 있지만.. 글이 너무 길어질 것 같아서 여기까지만 적는다.

 

코드는 아래와 같다.

 

"****** Function keys to compile and execute ******
au FileType cpp
	\ nnoremap <F5> :wa<CR>:!clear && g++ -std=c++17 -Wall -Wextra
	\ 	-Wfatal-errors -o ~/c/make % -DLOCAL_DEFINE |
	\ nnoremap <S-F5> :wa<CR>:!clear && g++ -std=c++17 -Wall -Wextra
	\	-Wfatal-errors -o ~/c/make % -DLOCAL_DEFINE -DDEBUG |
	\ nnoremap <F6> :wa<CR>:!clear && ~/c/make < ~/c/input.txt >
	\	~/c/output.txt |
	\ nnoremap <S-F6> :wa<CR>:!clear && ~/c/make < ~/c/input.txt |
	\ nnoremap <F7> :wa<CR>:!clear && ~/c/make |
	\ nnoremap <C-F5> :wa<CR>:!clear && g++ -std=c++17 -Wall -Wextra
	\	-Wfatal-errors -o ~/c/make2 % -DLOCAL_DEFINE |
	\ nnoremap <C-S-F5> :wa<CR>:!clear && g++ -std=c++17 -Wall -Wextra
	\	-Wfatal-errors -o ~/c/make2 % -DLOCAL_DEFINE -DDEBUG |
	\ nnoremap <C-S-F6> :wa<CR>:!clear && ~/c/make2 < ~/c/input.txt |
	\ nnoremap <C-F6> :wa<CR>:!clear && ~/c/make2 < ~/c/input.txt >
	\	~/c/output.txt |
	\ nnoremap <C-F7> :wa<CR>:!clear && ~/c/make2


"********** Function Keys in Insert Mode **********
au FileType cpp
	\ imap <F5> <ESC><F5> |
	\ imap <S-F5> <ESC><S-F5> |
	\ imap <F6> <ESC><F6> |
	\ imap <S-F6> <ESC><S-F6> |
	\ imap <F7> <ESC><F7> |
	\ imap <C-F5> <ESC><C-F5> |
	\ imap <C-S-F5> <ESC><C-S-F5> |
	\ imap <C-F6> <ESC><C-F6> |
	\ imap <C-S-F6> <ESC><C-S-F6> |
	\ imap <C-F7> <ESC><C-F7>


"********** :Base<cr> to Load base.cpp  **********
command! Base call CBase()
function! CBase()
	if (&ft == "cpp")
		execute "normal! :w"
		execute "!cp ~/c/library/base.cpp %"
		execute ":1"
	endif
endfunction


"******* Split Views for Input/Output Files*******
au FileType cpp
	\ nnoremap <C-p> :bo 32vs ~/c/output.txt <CR>:split ~/c/input.txt <CR> :w<CR> <C-w>h |
	\ nnoremap <C-o> :%bd\|e#<CR>


"******************* Key Binds *******************
inoremap {<ENTER> {<ENTER>}<ESC>ko
inoremap {<Space> {<Space><Space>}<ESC>hi
inoremap {<Tab> {<ENTER><ESC>o}<ESC>kA
inoremap {: {<Enter>}

inoremap <C-e> <ESC><C-e>
inoremap <C-y> <ESC><C-y>

"for long codes: scroll bind view
nnoremap <C-S-F7> :vsplit<CR> :exe "normal 2\< <BackSpace> C-W>w\< <BackSpace>C-F>"<CR> :windo setlocal scrollbind<CR>
nnoremap <C-S-F8> :windo setlocal noscrollbind<CR>

nnoremap <leader>t :w<CR>:TlistUpdate<CR>:TlistToggle<CR>

au FileType cpp
	\ nnoremap <leader>d o#ifdef DEBUG<cr>#endif<esc>k |
	\ inoremap <leader>d #ifdef DEBUG<cr>#endif<esc>k |
	\ nnoremap <leader>l :python3 print(get_cpp_library())<CR>

au FileType cpp
	\ nnoremap <leader>v /\/ ---- Woosung Song's Source Code ---- \/<cr> |
	\ nnoremap <leader>m o//////// ---- Woosung Song's Source Code ---- ////////<cr><esc> |
	\ inoremap <leader>m //////// ---- Woosung Song's Source Code ---- ////////


"***************** Abbreviations *****************
iabbrev algorihtm algorithm
iabbrev namespcae namespace
iabbrev ednl endl
iabbrev endll endl
iabbrev itn int





"************** Vim-Python Scripts **************
python3 << endpython

import vim, os

CPP_SIGNATURE = '//////// ---- Woosung Song\'s Source Code ---- ////////'
CPP_FOLD_BRACKET_START = '{'*3
CPP_FOLD_BRACKET_END = '}'*3
CPP_CODE_DIRECTORY = '/home/woosung/c/library/'
CPP_EXTENSION = '.cpp'
CPP_FOLD = True
CPP_BASE_FILE = CPP_CODE_DIRECTORY + 'base.cpp'
CPP_NOT_CODES = ['help', 'base', '', 'tmp']

def get_escaped_string(s):
	s = s.replace('\\', '\\\\')
	s = s.replace('\'', '\'\'')
	return s

def get_cpp_line_number(s):
	l = 0
	lines = s.split('\n')
	folded = 0
	for line in lines:
		if folded == 0:
			l += 1
		elif line.find(CPP_FOLD_BRACKET_END) != -1:
			folded -= 1
		if line.find(CPP_FOLD_BRACKET_START) != -1:
			folded += 1
	print(l)
	return l

def get_cpp_library():
	cursor = vim.eval("expand('<cword>')").strip()
	if cursor == "":
		print(show_cpp_library())
		return ''
	try:
		with open(CPP_CODE_DIRECTORY + cursor + CPP_EXTENSION, 'r') as f:
			s = f.read()
			st = s.find(CPP_SIGNATURE)
			ed = s.rfind(CPP_SIGNATURE) + len(CPP_SIGNATURE) + 1
			code = get_escaped_string(s[st:ed])
			vim.command("execute 'normal! cc'")
			vim.command("execute 'normal! I// library ''" + cursor + CPP_EXTENSION + "'''")
			vim.command("execute 'normal! o'")
			vim.eval("setreg('@', '" + code + "')")
			vim.command("execute 'normal! pkkzf" + str(get_cpp_line_number(code)) + "j'")
			f.close()
			return 'the library \'' + cursor + '\' has been successfully loaded!!'
	except IOError:
		print('the library \'' + cursor + '\' doesn\'t exist!!')
		print(show_cpp_library())
		return ''

def show_cpp_library(split=4, word_width=24):
	files = [f[:-4] for f in os.listdir(CPP_CODE_DIRECTORY)]
	files.sort()
	cnt, pr = 0, "Available Libraries:\n" + ("-" * (word_width * split)) + "\n"
	for name in files:
		if name in CPP_NOT_CODES: continue
		if name[0] == '.': continue
		cnt += 1
		pr += name + (" " * max(0, word_width - len(name)))
		if cnt % split == 0:
			pr += "\n"
	if cnt % word_width != 0:
		pr += "\n"
	print(pr)

endpython

 

아래 파이썬 코드만 좀 빼면 알아보기 어렵지는 않은 코드다.

 

Woosung 혹은 woosung 은 내 이름이자 컴퓨터 사용자 이름이다. 각자 맞게 수정해주자. 남이 이 이름 그대로 쓰면 좀 무서울 것 같다..

 

아마 이 코드대로 넣고 사용한다면 컴파일-실행 동작과 컨트롤 P, 컨트롤 O 단축키까지는 잘 먹을 것이다.

(만약 안 된다면 mkdir ~/c를 해주자.)

 

 

라이브러리 제작 및 불러오기

위에서 설명한 바에 따르면 모든 라이브러리는 ~/c/library 폴더 내에 저장되어 있고 특정한 양식을 가져야한다고 했다.

당장 내 라이브러리는 이렇게 구성되어 있다.

 

https://github.com/lego0901/CP_Library

 

lego0901/CP_Library

C++ library for competitive programming. Contribute to lego0901/CP_Library development by creating an account on GitHub.

github.com

그대로 사용하는 것은 권장하지 않는다. 내 이름이 워터마크로 표시되어 있는데 남 코드에서 그게 발견되면 되게 무서울 것 같다.

표절 문제도 걸릴까봐 약간 무섭긴 하다. (공공연하게 공개함으로써 코드포스 서드파티 사용 규칙을 준수하므로 나는 상관없긴 하지만..)

다만 이 라이브러리가 어떻게 구성되는지 알면 누구나 자기 것을 만들 수 있으니까 설명하겠다.

대표적으로 짧고 깔끔한 union_find.cpp를 예를 들어 설명하면

 

~/c/library/union_find.cpp

이렇게 union_find 클래스 내용이 /////// ---- Woosung Song's Source Code ---- //////// 대충 이런 표식을 위 아래로 둘러쌓여있다. 이 코드는 .cvimrc에 나왔던 그 문자열이다.

 

.cvimrc에 있던 파이썬 코드가 이 표식을 파일에서 찾아 사이에 있는 class 부분만 복사-붙여넣기 해준다.

그렇기 때문에 위 아래로 있는 #include <bits/stdc++.h>나 int main() 테스트 코드 등은 복사되지 않는다.

 

여러 라이브러리 간 충돌 문제를 피하기 위해 #ifndef, #define 을 적절하게 활용하였다. 라이브러리가 복잡해지고 상호 의존도가 높아질 수록 필수적이라 생각한다.

 

Vim에서 코드 접기 기능에 대한 설명은 https://m.blog.naver.com/PostView.nhn?blogId=itsgood3&logNo=40017941567&proxyReferer=https%3A%2F%2Fwww.google.com%2F 이 포스팅이 최고인 것 같다. 코드 접기 기능이 생소하면 한 번 쯤 정독하자.

 

.vimrc 파일에서 foldmethod를 marker로 유지했기 때문에 한 번 접은 코드는 다시 열어도 졉혀있게 된다.

 

표식 문자열은 역슬래시 M을 누르면 자동으로 삽입된다. 표식 문자열 자체를 내 입맛대로 바꾸고 싶으면 .cvimrc 파일을 편집해서 Woosung 부분을 찾아 바꾸면 될 것 같은 부분을 다 바꾸면 된다.

 

이러한 코드들을 ~/c/library 폴더 내에 많이 저장해두면 된다!

 

그 상태에서 코드를 짜다가 전역변수 선언하는 곳(즉 int main() 함수 위. class, struct, namespace를 선언하는 곳)에 union_find 라고 입력하고 역슬래시 L을 누르면 코드가 예쁘게 삽입된다.

 

 

사용효과

사실 이 글은 Vim을 너무 메모장처럼만 사용하시는 분들이 너무 많은 것 같아 올렸다. iabbrev, map, function, macro 등 기본적인 기능만 알아도 타이핑 양이 확 줄어드는데, 잘 활용 못하시고 "아니 도대체 Vim을 왜 권장하는거야?"라고 툴툴대는 분들께 Vim의 매력을 알려주고 열심히 익힐 동기를 제공해드리고자 하는데 목적이 있다.

 

당연히 이런 스크립팅이 없으면 Vim은 메모장보다 더 불편하다.. 그런 면에서 진입장벽이 꽤 높은건 사실인 것 같다.

하지만 조금만 극복하면 정말 편리하다. 이렇게 쓰다가 코드블락이나 Dev C++ 쓰면 답답한걸 보면 참 신기하다.

 

이 게시물에서 단축키로 만들어 편해진 내용은 다음과 같다.

 

  • 컴파일 - 입/출력 화면 split 기능으로 원시적 Vim보다는 한결 나아졌다.
  • 라이브러리 기능 덕에 코딩 속도가 확실히 빨라졌다.

무슨 라이브러리를 만들어야할지 모르겠다시는 분들은 인터넷에 돌아다니는 고수들의 경시대회 팀 노트들을 참고하길 바란다. 아마 koosaga, zigui, myungwoo 같은 대단하신 분들의 블로그에 하나 씩 있을 것이다. 똑같이 만들면 안 되겠지만 그 코드를 이해하고 체화하는 과정에서 정말 많이 배운다.

나는 같이 ICPC 나갔던 kajebiii 의 깃헙에 있는 팀 노트를 기준으로 많이 편성했다.(그립읍니다..) 팀노트에 있는 알고리즘을 보고 스스로 사용할 수 있게 옮기는 것만으로도 제법 공부가 된다.

 

비판

간혹 라이브러리를 만들어 사용하는 것을 지적하는 분들이 있다.

 

코드포스 블로그에 종종 "고수 분들은 라이브러리 얼마나 사용하시나요?" 물어보는 사람들 있는데

"난 하나도 안 써요. 그걸 만들만큼 근면하지 않거든요." 혹은 "라이브러리를 만들면 생각하는 힘이 줄어들어요"라고 시크하게 답변하는 레드 코더들은 정말 멋있다.

 

하지만 나는 라이브러리를 사용하는 방식이 괜찮다고 생각한다. 코딩 문제를 조금 더 수학적인 차원에서 가지고 놀 수 있기 때문이다.

 

이게 무슨말이냐고 하면, 나는 원래 수학을 했던 사람이라 문제 풀이를 할 때 집합 단위로 사고하는 것이 편하다.

하지만 프로그래밍 경시대회 해답은 결국 코드로 내야하기 때문에 그런 추상적 모델들을 모두 적절한 자료구조로 옮겨야한다.

아직 수준이 시원치 않아 그런 구현 능력이 떨어지는 것이 사실이다.. 당장 $k$번째 수 구하기만 해도 쉽지 않다. 잔실수도 많다.

 

하지만 라이브러리로 이런 자료구조들이 확실하게 만들어져있다는 것이 보장이 되면 더 어려운 것을 고민하는데 더 집중할 수 있다.

수학적 추상화 모델과 코드 간의 거리가 좁아진다고 해야하나.. 아무튼 수학에서만 놀아도 '이거 도대체 어떻게 구현하지..' 같은 고민을 덜 하게 된다.

확실히 어려운 문제를 풀 때 코딩 고민보다 수학 고민을 더 많이하게되는 것 같아 실력 향상에 크게 도움이 되었다.

어차피 Hungarian만 쓰면 풀리는 문제, FFT만 쓰면 풀리는 문제 등은 시험에 안 나온다. 결국 알고있는 지식을 응용하는 문제들이 나오지, 그 알고리즘 자체로만 해결되는 문제는 안 나와서 라이브러리 사용 자체가 사고력 연습을 저해하는 것 같지는 않다.

또한 '잘 알려진 자료구조나 알고리즘'을 빠르고 정확하게 구현하는걸 그렇게 높게 인정해줘야할까.. 취향 차이이기는 하지만 개인적으로 학교 시험도 오픈북으로 어려운 문제들이 나오는 것을 선호하는 편이다. 차라리 여기서 생각하는 자원을 아끼고 더 어려운 고민을 하는 것이 낫다.

또한 라이브러리를 직접 만드는 과정에서 코드를 계속 검증하면서 코드 구현력 자체도 향상된 것 같기도 하다.

 

물론 오프라인 대회를 나가면 다시 처음부터 짜야하니까 나같은 사람이 불리할 수도 있겠다만

어짜피 FFT, Geometry, Splay Tree 같은건 대부분 팀노트 보고 베껴서 짜니까.. 뭐 상관없지 않을까? 하는 생각도 든다.

 

암튼 더 많은 사람들이 쉬운 문제에서 골머리를 겪기보다 어려운 문제를 고민하는데 시간을 많이 쓰면 좋겠다.