<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>FFT on Jiho Kim</title><link>https://blog.wlgh7407.com/tags/fft/</link><description>Recent content in FFT on Jiho Kim</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 06 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.wlgh7407.com/tags/fft/index.xml" rel="self" type="application/rss+xml"/><item><title>NTT(Number Theoretic Transform) - 정수론적 변환</title><link>https://blog.wlgh7407.com/posts/algorithm/topics/260406_til_nttnumber-theoretic-transform---%EC%A0%95%EC%88%98%EB%A1%A0%EC%A0%81-%EB%B3%80%ED%99%98/</link><pubDate>Mon, 06 Apr 2026 00:00:00 +0000</pubDate><guid>https://blog.wlgh7407.com/posts/algorithm/topics/260406_til_nttnumber-theoretic-transform---%EC%A0%95%EC%88%98%EB%A1%A0%EC%A0%81-%EB%B3%80%ED%99%98/</guid><description>&lt;h2 id="-상세-정리"&gt;&lt;a href="#-%ec%83%81%ec%84%b8-%ec%a0%95%eb%a6%ac" class="header-anchor"&gt;&lt;/a&gt;📝 상세 정리
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.wlgh7407.com/posts/algorithm/topics/260402_til_fft---%EA%B3%A0%EC%86%8D-%ED%91%B8%EB%A6%AC%EC%97%90-%EB%B3%80%ED%99%98/" &gt;FFT&lt;/a&gt;를 잘 모른다면, 먼저 읽고 오자.&lt;/li&gt;
&lt;li&gt;다항식의 곱셈 결과를 특정 소수 $p$로 나눈 나머지를 구해야하거나, 값이 커지거나 해서 실수 오차가 발생하는 경우가 있다.
&lt;ul&gt;
&lt;li&gt;실수를 안쓰고 FFT를 할 수는 없을까?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;우리가 FFT에서 핵심으로 사용하던 $\omega = \exp\left(-i\dfrac{2\pi}{N}\right)$를 조금 더 생각해보자.
&lt;ul&gt;
&lt;li&gt;FFT에서 사용되는 핵심적인 성질은 $\omega^N = 1$ 이고, $1, \omega, \omega^2, \cdots, \omega^{N-1}$이 모두 다르다는 점이다.&lt;/li&gt;
&lt;li&gt;그러면 모듈러 합동식의 관점에서, 어떤 수 $k$와 어떤 소수 $p$가 있어서 $k^N \equiv 1 \pmod p$를 만족시키고, $1, g, g^2, \cdots, g^{N-1} \pmod p$이 모두 다르다면, FFT와 같은 느낌으로 사용할 수 있지 않을까?
&lt;ul&gt;
&lt;li&gt;이러한 수 $g$를 &lt;strong&gt;원시근&lt;/strong&gt;이라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그러면 $N$을 또 잘 잡아야할 것 같은데, 페르마의 소정리를 이용할 수 있을 것 같다.&lt;/li&gt;
&lt;li&gt;$a^{p-1} \equiv 1 \pmod p$ 이므로, $N = p-1$이면 좋을 것 같다.&lt;/li&gt;
&lt;li&gt;FFT를 반으로 쪼개던 방법을 잘 이용하기 위해선, $N$이 $2$로 충분히 많이 나누어 떨어지면 좋겠다.
&lt;ul&gt;
&lt;li&gt;$N = a \times 2^b$ 꼴이면 좋겠고, 이에 따라 $p = a \times 2^b + 1$ 이면 좋겠다.
&lt;ul&gt;
&lt;li&gt;$N$을 $2$로 나누어 떨어지게 할 수 있는 횟수에 따라 FFT로 계산 가능한 배열의 크기가 달라진다!&lt;/li&gt;
&lt;li&gt;$n \leq 2^b$ 의 크기의 다항식까지만 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이로 어울리는 대표적인 소수로는 $p = 998244353 = 119 \times 2^{23} + 1$ 이 있겠다.
&lt;ul&gt;
&lt;li&gt;이때 원시근 $g = 3$ 을 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그러면 이제 $\omega = \exp\left(-i\dfrac{2\pi}{N}\right)$ 대신 $\omega = g^{(p-1)/n}$ 을 이용해서 FFT를 수행할 수 있게 되었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이를 구현하면 다음과 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ntt&lt;/span&gt;(vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;mint&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;a, &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; inv &lt;span style="color:#f92672"&gt;=&lt;/span&gt; false){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; N &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a.size();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	assert(N &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; (N &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; (N&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// N은 2의 거듭제곱이어야 함
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// 비트 뒤집기
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, j &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; N; i&lt;span style="color:#f92672"&gt;++&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; bit &lt;span style="color:#f92672"&gt;=&lt;/span&gt; N &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(; j &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; bit; bit &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) j &lt;span style="color:#f92672"&gt;^=&lt;/span&gt; bit;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		j &lt;span style="color:#f92672"&gt;^=&lt;/span&gt; bit;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; j) swap(a[i], a[j]);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// 단위근 미리 계산
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	mint ang &lt;span style="color:#f92672"&gt;=&lt;/span&gt; mint(&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;).pow((MOD&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)&lt;span style="color:#f92672"&gt;/&lt;/span&gt;N);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(inv) ang &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ang.inv();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;mint&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; w(N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// 단위근
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	w[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	rep(i, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;) w[i] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; w[i&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;] &lt;span style="color:#f92672"&gt;*&lt;/span&gt; ang;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// Cooley-Tukey FFT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; len &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;; len &lt;span style="color:#f92672"&gt;&amp;lt;=&lt;/span&gt; N; len &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; step &lt;span style="color:#f92672"&gt;=&lt;/span&gt; N &lt;span style="color:#f92672"&gt;/&lt;/span&gt; len;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; N; i &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; len){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			rep(j, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, len&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				mint even &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a[i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;j], odd &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a[i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;j&lt;span style="color:#f92672"&gt;+&lt;/span&gt;(len&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)] &lt;span style="color:#f92672"&gt;*&lt;/span&gt; w[j&lt;span style="color:#f92672"&gt;*&lt;/span&gt;step];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				a[i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;j] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; even &lt;span style="color:#f92672"&gt;+&lt;/span&gt; odd;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				a[i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;j&lt;span style="color:#f92672"&gt;+&lt;/span&gt;(len&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; even &lt;span style="color:#f92672"&gt;-&lt;/span&gt; odd;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// 역 FFT인 경우 결과를 N으로 나눔
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(inv){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		mint invN &lt;span style="color:#f92672"&gt;=&lt;/span&gt; mint(N).inv();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N) a[i] &lt;span style="color:#f92672"&gt;*=&lt;/span&gt; invN;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="질문-사항"&gt;&lt;a href="#%ec%a7%88%eb%ac%b8-%ec%82%ac%ed%95%ad" class="header-anchor"&gt;&lt;/a&gt;❔질문 사항
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="-참고-자료"&gt;&lt;a href="#-%ec%b0%b8%ea%b3%a0-%ec%9e%90%eb%a3%8c" class="header-anchor"&gt;&lt;/a&gt;🔗 참고 자료
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://rkm0959.tistory.com/187" target="_blank" rel="noopener"
 &gt;https://rkm0959.tistory.com/187&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://algoshitpo.github.io/2020/05/20/fft-ntt/" target="_blank" rel="noopener"
 &gt;https://algoshitpo.github.io/2020/05/20/fft-ntt/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>BOJ 14958 Rock Paper Scissors</title><link>https://blog.wlgh7407.com/posts/algorithm/ps/260405_algorithm_boj-14958-rock-paper-scissors/</link><pubDate>Sun, 05 Apr 2026 00:00:00 +0000</pubDate><guid>https://blog.wlgh7407.com/posts/algorithm/ps/260405_algorithm_boj-14958-rock-paper-scissors/</guid><description>&lt;h2 id="-문제-정보"&gt;&lt;a href="#-%eb%ac%b8%ec%a0%9c-%ec%a0%95%eb%b3%b4" class="header-anchor"&gt;&lt;/a&gt;📝 문제 정보
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;링크: &lt;a class="link" href="https://www.acmicpc.net/problem/14958" target="_blank" rel="noopener"
 &gt;https://www.acmicpc.net/problem/14958&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="번역"&gt;&lt;a href="#%eb%b2%88%ec%97%ad" class="header-anchor"&gt;&lt;/a&gt;번역
&lt;/h3&gt;&lt;h4 id="문제"&gt;&lt;a href="#%eb%ac%b8%ec%a0%9c" class="header-anchor"&gt;&lt;/a&gt;문제
&lt;/h4&gt;&lt;p&gt;가위바위보(RPS) 기계가 있으며, 이 기계는 가위, 바위, 보 중 하나를 무작위로 생성한다. 당신도 비슷한 소형 가위바위보 기계를 가지고 있다. 게임 전에, RPS 기계는 길이 $n$의 가위바위보 선택 목록을 생성하고, 당신의 기계도 길이 $m$의 선택 목록을 생성한다. 즉, RPS 기계의 전체 선택 목록과 당신의 기계의 선택 목록을 모두 알고 있다. 물론, 각 기계의 선택은 세 가지 옵션(가위, 바위, 보) 중 하나이다.&lt;/p&gt;
&lt;p&gt;이제 가위바위보 게임을 시작한다. 매 대결에서 RPS 기계의 선택 목록과 당신의 기계의 선택 목록을 순서대로 비교하여 어느 쪽이 이기는지 판정한다. 단, 당신은 가장 많은 승리 횟수를 얻기 위해 RPS 기계의 선택 일부를 건너뛸 수 있다. 대결을 시작하기로 결정한 후에는 대결이 끝날 때까지 건너뛸 수 없다. &lt;code&gt;R&lt;/code&gt;은 바위, &lt;code&gt;P&lt;/code&gt;는 보, &lt;code&gt;S&lt;/code&gt;는 가위를 나타낸다.&lt;/p&gt;
&lt;p&gt;예를 들어, RPS 기계의 목록이 &amp;ldquo;&lt;code&gt;RSPPSSSRRPPR&lt;/code&gt;&amp;ldquo;이고 당신의 기계의 목록이 &amp;ldquo;&lt;code&gt;RRRR&lt;/code&gt;&amp;ldquo;라고 하자. 최대 승리 횟수를 얻으려면, RPS 기계의 선택을 3개 또는 4개 건너뛴 후 대결을 시작해야 한다. (Figure H.1 참조.) 그러면 3번 승리할 수 있다. 무승부는 고려하지 않는다.&lt;/p&gt;
&lt;p&gt;Figure H.1. $n = 12$, $m = 4$일 때 RPS 기계를 상대로 최대 승리를 얻는 위치.&lt;/p&gt;
&lt;p&gt;RPS 기계의 선택 목록과 당신의 선택 목록이 주어질 때, 최대 승리 횟수를 얻을 수 있는 위치를 구하시오.&lt;/p&gt;
&lt;h4 id="입력"&gt;&lt;a href="#%ec%9e%85%eb%a0%a5" class="header-anchor"&gt;&lt;/a&gt;입력
&lt;/h4&gt;&lt;p&gt;표준 입력으로 읽는다. 첫째 줄에 두 양의 정수 $n$과 $m$이 주어진다 ($1 \le m &lt; n \le 100{,}000$). $n$은 RPS 기계의 문자열 길이이고, $m$은 당신의 기계의 문자열 길이이다. 다음 줄에는 RPS 기계의 선택 목록이, 그다음 줄에는 당신의 기계의 선택 목록이 주어진다.&lt;/p&gt;
&lt;h4 id="출력"&gt;&lt;a href="#%ec%b6%9c%eb%a0%a5" class="header-anchor"&gt;&lt;/a&gt;출력
&lt;/h4&gt;&lt;p&gt;표준 출력으로 출력한다. 첫째 줄에 최대 승리 횟수를 나타내는 정수를 출력한다.&lt;/p&gt;
&lt;h2 id="-관찰-및-접근"&gt;&lt;a href="#-%ea%b4%80%ec%b0%b0-%eb%b0%8f-%ec%a0%91%ea%b7%bc" class="header-anchor"&gt;&lt;/a&gt;🧐 관찰 및 접근
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;약간 밀어서 합성곱을 최대화 하는 맛..
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.wlgh7407.com/posts/algorithm/ps/260402_algorithm_boj-25456-%EA%B6%81%EA%B8%88%ED%95%9C-%EC%8B%9C%ED%94%84%ED%8A%B8/" &gt;BOJ 25456 궁금한 시프트&lt;/a&gt;랑 비슷해보인다!&lt;/li&gt;
&lt;li&gt;그런데 0/1이 아니라 RSP인데&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;근데 그냥 RSP 따로 해서 &lt;a class="link" href="https://blog.wlgh7407.com/posts/algorithm/topics/260402_til_fft---%EA%B3%A0%EC%86%8D-%ED%91%B8%EB%A6%AC%EC%97%90-%EB%B3%80%ED%99%98/" &gt;FFT&lt;/a&gt;를 3번 돌리면 되지 않을까?&lt;/li&gt;
&lt;li&gt;최대 승리를 위한 위치가 아니라 최대 승리 횟수 자체만 승리하면 된다! 쉽네&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="-풀이"&gt;&lt;a href="#-%ed%92%80%ec%9d%b4" class="header-anchor"&gt;&lt;/a&gt;💻 풀이
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;코드 (C++):&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="paywall-wrapper"&gt;
 &lt;div class="paywall-content paywall-locked"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;solve&lt;/span&gt;(){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; N, M; cin &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; N &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; M;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; string S, T; cin &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; S &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; T;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; ans(N&lt;span style="color:#f92672"&gt;+&lt;/span&gt;M);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; string RPS &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;RPS&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; sz &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt;(sz &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; N&lt;span style="color:#f92672"&gt;+&lt;/span&gt;M) sz &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; A(sz), B(sz);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(j, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N) A[j] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; S[j] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; RPS[i];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(j, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, M) B[M&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;j] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; T[j] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; RPS[(i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)&lt;span style="color:#f92672"&gt;%&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FFT&lt;span style="color:#f92672"&gt;::&lt;/span&gt;fft(A);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FFT&lt;span style="color:#f92672"&gt;::&lt;/span&gt;fft(B);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(j, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, sz) A[j] &lt;span style="color:#f92672"&gt;*=&lt;/span&gt; B[j];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FFT&lt;span style="color:#f92672"&gt;::&lt;/span&gt;fft(A, true);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(j, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N&lt;span style="color:#f92672"&gt;+&lt;/span&gt;M&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) ans[j] &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; round(A[j].real());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; mx &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, M&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, N&lt;span style="color:#f92672"&gt;+&lt;/span&gt;M&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) mx &lt;span style="color:#f92672"&gt;=&lt;/span&gt; max(mx, ans[i]);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cout &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; mx;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/div&gt;
 &lt;div class="paywall-overlay"&gt;
 &lt;div class="paywall-icon"&gt;🔒&lt;/div&gt;
 &lt;p class="paywall-title"&gt;구현 코드 잠금&lt;/p&gt;
 &lt;p class="paywall-desc"&gt;아래 쿠팡 링크를 방문하시면 코드가 공개됩니다.&lt;br&gt;광고 수익이 블로그 운영에 도움이 됩니다 🙏&lt;/p&gt;
 &lt;a
 href="https://link.coupang.com/a/dOJ25W"
 target="_blank"
 rel="sponsored noopener"
 class="paywall-btn"
 onclick="paywallUnlock()"
 &gt;
 🛒 쿠팡 방문하고 코드 보기
 &lt;/a&gt;
 &lt;p class="paywall-hint"&gt;방문 후 잠금이 자동으로 해제됩니다&lt;/p&gt;
 &lt;/div&gt;
&lt;/div&gt;</description></item><item><title>BOJ 20176 Needle</title><link>https://blog.wlgh7407.com/posts/algorithm/ps/260404_algorithm_boj-20176-needle/</link><pubDate>Sat, 04 Apr 2026 00:00:00 +0000</pubDate><guid>https://blog.wlgh7407.com/posts/algorithm/ps/260404_algorithm_boj-20176-needle/</guid><description>&lt;h2 id="-문제-정보"&gt;&lt;a href="#-%eb%ac%b8%ec%a0%9c-%ec%a0%95%eb%b3%b4" class="header-anchor"&gt;&lt;/a&gt;📝 문제 정보
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;링크: &lt;a class="link" href="https://www.acmicpc.net/problem/20176" target="_blank" rel="noopener"
 &gt;https://www.acmicpc.net/problem/20176&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="번역"&gt;&lt;a href="#%eb%b2%88%ec%97%ad" class="header-anchor"&gt;&lt;/a&gt;번역
&lt;/h3&gt;&lt;h4 id="문제"&gt;&lt;a href="#%eb%ac%b8%ec%a0%9c" class="header-anchor"&gt;&lt;/a&gt;문제
&lt;/h4&gt;&lt;p&gt;&amp;ldquo;바늘&amp;quot;은 북쪽 왕국에 사는 전설적인 암살자이다. 알다시피, 바늘은 매우 가늘고 길다. 무엇보다, 치명적으로 날카롭다. 북쪽 왕국의 왕은 바늘이 수없이 찔러 자신을 죽일지도 모른다는 생각에 사로잡혀 있다. 왕은 바늘을 체포하라는 긴급 명령을 내렸다. 그리하여 바늘은 남쪽 왕국으로 탈출하기로 결심했다.&lt;/p&gt;
&lt;p&gt;아래 그림과 같이, 두 왕국 사이의 국경은 세 개의 수평 장벽(선분)으로 이루어져 있으며, 각 장벽에는 하나 이상의 극소 크기의 구멍이 뚫려 있다. (구멍은 그림에서 $\times$로 표시되어 있다.) 세 장벽은 길이가 같으며 그림과 같이 수직으로 나란히 정렬되어 있다. 위쪽 장벽은 가운데 장벽보다 1단위 위에, 가운데 장벽은 아래쪽 장벽보다 1단위 위에 위치한다. 두 왕국은 뚫을 수 없는 외벽으로 둘러싸여 있다. 각 왕국의 영토는 매우 넓어서, 바늘은 왕국 안에서 자유롭게 움직일(평행 이동 또는 회전) 수 있다. 바늘의 길이는 장벽 길이의 최소 두 배 이상이다. 바늘은 강체, 즉 구부러지지 않으며 두께가 없으므로, 구멍은 자유롭게 통과할 수 있지만 구멍 이외의 장벽 부분은 뚫을 수 없다.&lt;/p&gt;
&lt;p&gt;북쪽 왕국에서 남쪽 왕국으로 가는 유일한 방법은, 세 장벽 각각의 구멍 하나씩을 동시에 통과하는 것이다. 즉, 바늘은 세 장벽에서 각각 하나씩, 총 세 개의 구멍이 일직선 위에 놓여 있을 때에만 국경을 통과할 수 있다. 그림의 국경에서는 북쪽에서 남쪽으로 가는 탈출 통로가 두 개 존재한다.&lt;/p&gt;
&lt;p&gt;이 불쌍한 암살자를 위해, 북쪽 왕국에서 남쪽 왕국으로 가능한 탈출 통로의 수를 구하는 프로그램을 작성하라.&lt;/p&gt;
&lt;h4 id="입력"&gt;&lt;a href="#%ec%9e%85%eb%a0%a5" class="header-anchor"&gt;&lt;/a&gt;입력
&lt;/h4&gt;&lt;p&gt;표준 입력으로부터 읽는다. 입력은 여섯 줄로 구성된다. 첫 번째 줄에는 위쪽 장벽의 구멍 수를 나타내는 양의 정수 $n_u$가 주어진다. 두 번째 줄에는 구멍들의 $x$좌표를 나타내는 $n_u$개의 정수가 공백으로 구분되어 주어진다. 세 번째와 네 번째 줄은 가운데 장벽에 대한 것으로, 각각 가운데 장벽의 구멍 수 $n_m$과 $n_m$개의 구멍의 $x$좌표가 주어진다. 다섯 번째와 여섯 번째 줄은 아래쪽 장벽에 대한 것으로, 각각 아래쪽 장벽의 구멍 수 $n_l$과 $n_l$개의 구멍의 $x$좌표가 주어진다. $1 \leq n_u, n_m, n_l \leq 50{,}000$이며, 모든 구멍의 $x$좌표는 $-30{,}000$ 이상 $30{,}000$ 이하의 정수이다. 각 장벽의 구멍들은 서로 다른 $x$좌표를 가진다.&lt;/p&gt;
&lt;h4 id="출력"&gt;&lt;a href="#%ec%b6%9c%eb%a0%a5" class="header-anchor"&gt;&lt;/a&gt;출력
&lt;/h4&gt;&lt;p&gt;표준 출력으로 출력한다. 정확히 한 줄을 출력한다. 북쪽에서 남쪽으로 가능한 모든 통로의 수를 나타내는 음이 아닌 정수를 출력하라.&lt;/p&gt;
&lt;h2 id="-관찰-및-접근"&gt;&lt;a href="#-%ea%b4%80%ec%b0%b0-%eb%b0%8f-%ec%a0%91%ea%b7%bc" class="header-anchor"&gt;&lt;/a&gt;🧐 관찰 및 접근
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;윗쪽, 가운데, 아랫쪽에서 숫자 하나씩을 골랐을때, 등차수열을 이루면 되는 것 같다.&lt;/li&gt;
&lt;li&gt;윗쪽, 아랫쪽을 더해서 만들 수 있는 수들중에, 가운데 수의 두배가 되는 경우의 수를 찾자.&lt;/li&gt;
&lt;li&gt;더하는걸 나이브하게 하면 $O(N^2)$, 두 수를 고르고 이분탐색하거나 하면 $O(N^2\log N)$이지만..&lt;/li&gt;
&lt;li&gt;차수로 생각하면 $x$ 좌표의 범위에 대해 $O(X\log{X})$에 될 것 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="-풀이"&gt;&lt;a href="#-%ed%92%80%ec%9d%b4" class="header-anchor"&gt;&lt;/a&gt;💻 풀이
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;코드 (C++):&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="paywall-wrapper"&gt;
 &lt;div class="paywall-content paywall-locked"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;solve&lt;/span&gt;(){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; n1; cin &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; n1;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; v1(n1);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, n1) cin &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; v1[i];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; n2; cin &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; n2;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; v2(n2);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, n2) cin &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; v2[i];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; n3; cin &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; n3;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; v3(n3);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, n3) cin &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; v3[i];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; A(&lt;span style="color:#ae81ff"&gt;131072&lt;/span&gt;), B(&lt;span style="color:#ae81ff"&gt;131072&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; x: v1) A[x&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;30000&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; x: v3) B[x&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;30000&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FFT&lt;span style="color:#f92672"&gt;::&lt;/span&gt;fft(A);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FFT&lt;span style="color:#f92672"&gt;::&lt;/span&gt;fft(B);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;131072&lt;/span&gt;) A[i] &lt;span style="color:#f92672"&gt;*=&lt;/span&gt; B[i];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FFT&lt;span style="color:#f92672"&gt;::&lt;/span&gt;fft(A, true);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; ans &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; x: v2) ans &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; round(A[(x&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;30000&lt;/span&gt;)&lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;].real());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cout &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; ans &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/div&gt;
 &lt;div class="paywall-overlay"&gt;
 &lt;div class="paywall-icon"&gt;🔒&lt;/div&gt;
 &lt;p class="paywall-title"&gt;구현 코드 잠금&lt;/p&gt;
 &lt;p class="paywall-desc"&gt;아래 쿠팡 링크를 방문하시면 코드가 공개됩니다.&lt;br&gt;광고 수익이 블로그 운영에 도움이 됩니다 🙏&lt;/p&gt;
 &lt;a
 href="https://link.coupang.com/a/dOJ25W"
 target="_blank"
 rel="sponsored noopener"
 class="paywall-btn"
 onclick="paywallUnlock()"
 &gt;
 🛒 쿠팡 방문하고 코드 보기
 &lt;/a&gt;
 &lt;p class="paywall-hint"&gt;방문 후 잠금이 자동으로 해제됩니다&lt;/p&gt;
 &lt;/div&gt;
&lt;/div&gt;</description></item><item><title>BOJ 25456 궁금한 시프트</title><link>https://blog.wlgh7407.com/posts/algorithm/ps/260402_algorithm_boj-25456-%EA%B6%81%EA%B8%88%ED%95%9C-%EC%8B%9C%ED%94%84%ED%8A%B8/</link><pubDate>Thu, 02 Apr 2026 00:00:00 +0000</pubDate><guid>https://blog.wlgh7407.com/posts/algorithm/ps/260402_algorithm_boj-25456-%EA%B6%81%EA%B8%88%ED%95%9C-%EC%8B%9C%ED%94%84%ED%8A%B8/</guid><description>&lt;h2 id="-문제-정보"&gt;&lt;a href="#-%eb%ac%b8%ec%a0%9c-%ec%a0%95%eb%b3%b4" class="header-anchor"&gt;&lt;/a&gt;📝 문제 정보
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;링크: &lt;a class="link" href="https://www.acmicpc.net/problem/25456" target="_blank" rel="noopener"
 &gt;https://www.acmicpc.net/problem/25456&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="-관찰-및-접근"&gt;&lt;a href="#-%ea%b4%80%ec%b0%b0-%eb%b0%8f-%ec%a0%91%ea%b7%bc" class="header-anchor"&gt;&lt;/a&gt;🧐 관찰 및 접근
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;0과 1로 이루어진 두 문자열 $S, T$가 주어진다.&lt;/li&gt;
&lt;li&gt;두 문자열에 시프트 연산이 가능하다고 할때, 합성곱의 최댓값을 구하자.&lt;/li&gt;
&lt;li&gt;합성곱이므로 FFT를 의심해볼 수 있겠다.&lt;/li&gt;
&lt;li&gt;0과 1로 이루어진 문자열을 다항식으로 해석하면 된다.&lt;/li&gt;
&lt;li&gt;곱 결과가 같은 차수에 올 수 있도록 한 다항식의 계수들을 뒤집고, 시프트 연산을 위해 둘중 한 배열을 두배로 늘려서 계산하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="-풀이"&gt;&lt;a href="#-%ed%92%80%ec%9d%b4" class="header-anchor"&gt;&lt;/a&gt;💻 풀이
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;코드 (C++):&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="paywall-wrapper"&gt;
 &lt;div class="paywall-content paywall-locked"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;solve&lt;/span&gt;(){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; string S, T;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cin &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; S &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; T;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; N &lt;span style="color:#f92672"&gt;=&lt;/span&gt; S.size();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; sz &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt;(sz &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;N) sz &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; A(sz), B(sz);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N) &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(S[i] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;) A[i] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N) &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(S[i] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;) A[i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;N] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N) &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(T[i] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;) B[N&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;i] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FFT&lt;span style="color:#f92672"&gt;::&lt;/span&gt;fft(A);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FFT&lt;span style="color:#f92672"&gt;::&lt;/span&gt;fft(B);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, sz) A[i] &lt;span style="color:#f92672"&gt;*=&lt;/span&gt; B[i];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FFT&lt;span style="color:#f92672"&gt;::&lt;/span&gt;fft(A, true);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; ans &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, N&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;N&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) ans &lt;span style="color:#f92672"&gt;=&lt;/span&gt; max(ans, (&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;)round(A[i].real()));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// rep(i, 0, 3*N) cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &amp;#39; &amp;#39; &amp;lt;&amp;lt; A[i] &amp;lt;&amp;lt; &amp;#39;\n&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cout &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; ans &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/div&gt;
 &lt;div class="paywall-overlay"&gt;
 &lt;div class="paywall-icon"&gt;🔒&lt;/div&gt;
 &lt;p class="paywall-title"&gt;구현 코드 잠금&lt;/p&gt;
 &lt;p class="paywall-desc"&gt;아래 쿠팡 링크를 방문하시면 코드가 공개됩니다.&lt;br&gt;광고 수익이 블로그 운영에 도움이 됩니다 🙏&lt;/p&gt;
 &lt;a
 href="https://link.coupang.com/a/dOJ25W"
 target="_blank"
 rel="sponsored noopener"
 class="paywall-btn"
 onclick="paywallUnlock()"
 &gt;
 🛒 쿠팡 방문하고 코드 보기
 &lt;/a&gt;
 &lt;p class="paywall-hint"&gt;방문 후 잠금이 자동으로 해제됩니다&lt;/p&gt;
 &lt;/div&gt;
&lt;/div&gt;</description></item><item><title>FFT - 고속 푸리에 변환</title><link>https://blog.wlgh7407.com/posts/algorithm/topics/260402_til_fft---%EA%B3%A0%EC%86%8D-%ED%91%B8%EB%A6%AC%EC%97%90-%EB%B3%80%ED%99%98/</link><pubDate>Thu, 02 Apr 2026 00:00:00 +0000</pubDate><guid>https://blog.wlgh7407.com/posts/algorithm/topics/260402_til_fft---%EA%B3%A0%EC%86%8D-%ED%91%B8%EB%A6%AC%EC%97%90-%EB%B3%80%ED%99%98/</guid><description>&lt;h2 id="-상세-정리"&gt;&lt;a href="#-%ec%83%81%ec%84%b8-%ec%a0%95%eb%a6%ac" class="header-anchor"&gt;&lt;/a&gt;📝 상세 정리
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;이제 슬슬 FFT를 이해할 때가 된것 같다. 정리해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="푸리에-변환"&gt;&lt;a href="#%ed%91%b8%eb%a6%ac%ec%97%90-%eb%b3%80%ed%99%98" class="header-anchor"&gt;&lt;/a&gt;푸리에 변환
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;여러 사인파 / 코사인파가 더해진 함수 $h(t)$가 있다.
&lt;ul&gt;
&lt;li&gt;일단 먼저 사인파만 있다고 가정해보자.&lt;/li&gt;
&lt;li&gt;우리는 이 함수 $h(t)$에서 어떤 주파수를 가진 성분들이 있는지 검사하고싶다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;더 쉽게, 먼저 사인파 하나에 대해서 생각해보자.
&lt;ul&gt;
&lt;li&gt;함수 $f(x) = \sin{ax}$가 있다.
&lt;ul&gt;
&lt;li&gt;우리는 $a$를 모른다고 가정하자.&lt;/li&gt;
&lt;li&gt;어떻게 하면 이 $a$를 알아낼 수 있을까?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;임의의 사인파 $g(x) = \sin{bx}$를 곱해보자.
&lt;ul&gt;
&lt;li&gt;삼각함수의 곱셈정리에 의해&lt;/li&gt;
&lt;li&gt;$f(x)g(x) = \sin{ax}\sin{bx} = \frac{\cos{((a-b)x)} - \cos{((a+b)x)}}{2}$
&lt;ul&gt;
&lt;li&gt;위와 같이 나타낼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이를 음의 무한대부터 양의 무한대까지 적분하면, 주기함수의 특성을 이용해서 $a, b$가 같은지 다른지 알아낼 수 있다.
&lt;ul&gt;
&lt;li&gt;$a, b$는 주파수기에 $a, b &gt; 0$이 보장된다.&lt;/li&gt;
&lt;li&gt;$a = b$일 때만 양의 무한대로 발산하고, 그 외의 경우 진동발산한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;위의 특징은 여러 사인파의 합 $h(t)$에 대해서도 간단하게 성립한다.&lt;/li&gt;
&lt;li&gt;코사인 파가 섞여있다면 코사인파를 곱해서 같은 방법으로 수행해주면 되겠다.&lt;/li&gt;
&lt;li&gt;이 때, 오일러 공식 $e^{ix} = \cos{x} + i\sin{x}$을 이용해서 실수부 / 허수부로 나눠서 더 쉽게 계산할 수도 있다.&lt;/li&gt;
&lt;li&gt;이를 식으로 나타내면 다음과 같다.
&lt;ul&gt;
&lt;li&gt;
$$\hat{h}(x) = \int_{-\infty}^{\infty}{h(t)e^{-2\pi itx}dt}$$&lt;/li&gt;
&lt;li&gt;이는 신호 -&amp;gt; 주파수로의 변환으로 해석하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;반대로 주파수 -&amp;gt; 신호의 변환도 가능하다.
&lt;ul&gt;
&lt;li&gt;
$$h(t) = \int_{-\infty}^{\infty}{\hat{h}(x)e^{2\pi itx}dx}$$&lt;/li&gt;
&lt;li&gt;이를 푸리에 역변환이라고 하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;여기서 갖고 놀아보자. 이해할때 클로드가 만들어줬다.
&lt;div class="fv-wrap"&gt;
 &lt;style&gt;
 .fv-wrap {
 background: transparent;
 color: #e0e0e0;
 font-family: "Segoe UI", sans-serif;
 margin: 1.5rem 0;
 }
 .fv-wrap .fv-controls {
 display: flex;
 gap: 12px;
 flex-wrap: wrap;
 align-items: center;
 margin-bottom: 12px;
 padding: 10px 12px;
 background: #1a1d27;
 border-radius: 8px;
 }
 .fv-wrap .fv-controls label {
 font-size: 0.78rem;
 color: #aaa;
 }
 .fv-wrap .fv-controls select {
 background: #252836;
 color: #e0e0e0;
 border: 1px solid #333;
 border-radius: 4px;
 padding: 4px 8px;
 font-size: 0.8rem;
 }
 .fv-wrap .fv-section {
 margin-bottom: 16px;
 background: #1a1d27;
 border-radius: 8px;
 overflow: hidden;
 }
 .fv-wrap .fv-section-title {
 color: #7eb8f7;
 font-size: 0.85rem;
 font-weight: 600;
 padding: 8px 12px 2px;
 margin: 0;
 border: none;
 }
 .fv-wrap .fv-section-desc {
 color: #666;
 font-size: 0.75rem;
 padding: 0 12px 6px;
 margin: 0;
 }
 .fv-wrap canvas {
 width: 100%;
 height: 160px;
 display: block;
 }
 &lt;/style&gt;

 &lt;div class="fv-controls"&gt;
 &lt;label&gt;신호 종류: &lt;/label&gt;
 &lt;select id="fv-signalType"&gt;
 &lt;option value="finite_sine"&gt;유한 사인파 (현실적)&lt;/option&gt;
 &lt;option value="two_freq"&gt;두 주파수 합성&lt;/option&gt;
 &lt;option value="square"&gt;구형파&lt;/option&gt;
 &lt;option value="infinite_sine"&gt;무한 사인파 (이상적)&lt;/option&gt;
 &lt;/select&gt;
 &lt;/div&gt;

 &lt;div class="fv-section"&gt;
 &lt;p class="fv-section-title"&gt;① 시간 도메인 — h(t)&lt;/p&gt;
 &lt;p class="fv-section-desc"&gt;실제 데이터 (샘플링된 이산 값들)&lt;/p&gt;
 &lt;canvas id="fv-timeCanvas"&gt;&lt;/canvas&gt;
 &lt;/div&gt;

 &lt;div class="fv-section"&gt;
 &lt;p class="fv-section-title"&gt;② 주파수 도메인 — |X[k]| (DFT 결과)&lt;/p&gt;
 &lt;p class="fv-section-desc"&gt;
 각 주파수 성분의 크기. 스파이크가 있는 곳 = 해당 주파수 존재
 &lt;/p&gt;
 &lt;canvas id="fv-freqCanvas"&gt;&lt;/canvas&gt;
 &lt;/div&gt;

 &lt;script&gt;
 (function () {
 const N = 256;
 const wrap = document.currentScript.closest(".fv-wrap");
 const select = wrap.querySelector("#fv-signalType");

 function getSignal(type) {
 const x = new Array(N);
 for (let n = 0; n &lt; N; n++) {
 const t = n / N;
 if (type === "finite_sine") {
 const win = Math.sin(Math.PI * t);
 x[n] = win * Math.sin(2 * Math.PI * 5 * t);
 } else if (type === "two_freq") {
 const win = Math.sin(Math.PI * t);
 x[n] =
 win *
 (Math.sin(2 * Math.PI * 5 * t) +
 0.6 * Math.sin(2 * Math.PI * 13 * t));
 } else if (type === "square") {
 x[n] = Math.sin(2 * Math.PI * 5 * t) &gt; 0 ? 1 : -1;
 } else if (type === "infinite_sine") {
 x[n] = Math.sin(2 * Math.PI * 5 * t);
 }
 }
 return x;
 }

 function dft(x) {
 const re = new Array(N).fill(0);
 const im = new Array(N).fill(0);
 for (let k = 0; k &lt; N; k++) {
 for (let n = 0; n &lt; N; n++) {
 const angle = (-2 * Math.PI * k * n) / N;
 re[k] += x[n] * Math.cos(angle);
 im[k] += x[n] * Math.sin(angle);
 }
 }
 return { re, im };
 }

 function magnitude(re, im) {
 return re.map((r, i) =&gt; Math.sqrt(r * r + im[i] * im[i]));
 }

 function drawSignal(canvas, data, color) {
 const dpr = window.devicePixelRatio || 1;
 canvas.width = canvas.offsetWidth * dpr;
 canvas.height = 160 * dpr;
 const ctx = canvas.getContext("2d");
 ctx.scale(dpr, dpr);
 const W = canvas.offsetWidth,
 H = 160;

 ctx.fillStyle = "#1a1d27";
 ctx.fillRect(0, 0, W, H);

 ctx.strokeStyle = "#333";
 ctx.lineWidth = 1;
 ctx.beginPath();
 ctx.moveTo(0, H / 2);
 ctx.lineTo(W, H / 2);
 ctx.stroke();

 const max = Math.max(...data.map(Math.abs)) || 1;
 const show = data.slice(0, Math.floor(data.length / 2));
 const step = W / show.length;

 ctx.strokeStyle = color;
 ctx.lineWidth = 1.5;
 ctx.beginPath();
 show.forEach((v, i) =&gt; {
 const x = i * step;
 const y = H / 2 - (v / max) * (H / 2 - 12);
 if (i === 0) ctx.moveTo(x, y);
 else ctx.lineTo(x, y);
 });
 ctx.stroke();
 }

 function drawFreq(canvas, mag) {
 const dpr = window.devicePixelRatio || 1;
 canvas.width = canvas.offsetWidth * dpr;
 canvas.height = 160 * dpr;
 const ctx = canvas.getContext("2d");
 ctx.scale(dpr, dpr);
 const W = canvas.offsetWidth,
 H = 160;

 ctx.fillStyle = "#1a1d27";
 ctx.fillRect(0, 0, W, H);

 const half = mag.slice(0, Math.floor(N / 2));
 const max = Math.max(...half) || 1;
 const barW = W / half.length;

 half.forEach((v, k) =&gt; {
 const h = (v / max) * (H - 20);
 const x = k * barW;
 const intensity = v / max;
 const r = Math.floor(intensity * 255);
 const g = Math.floor(intensity * 140);
 const b = Math.floor((1 - intensity) * 220 + 35);
 ctx.fillStyle = `rgb(${r},${g},${b})`;
 ctx.fillRect(x, H - h, barW - 1, h);
 });

 ctx.fillStyle = "#666";
 ctx.font = "10px sans-serif";
 [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120].forEach((k) =&gt; {
 const x = (k / (N / 2)) * W;
 ctx.fillText(k, x, H - 2);
 });
 ctx.fillStyle = "#555";
 ctx.fillText("주파수 k →", W - 70, H - 2);
 }

 function draw() {
 const type = select.value;
 const signal = getSignal(type);
 const { re, im } = dft(signal);
 const mag = magnitude(re, im);
 drawSignal(wrap.querySelector("#fv-timeCanvas"), signal, "#7eb8f7");
 drawFreq(wrap.querySelector("#fv-freqCanvas"), mag);
 }

 select.addEventListener("change", draw);
 window.addEventListener("resize", draw);
 draw();
 })();
 &lt;/script&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;복소평면에서의 이해도 가능하다. 주파수가 다르다면 상쇄되어 없어진다.
&lt;div class="fcv-wrap"&gt;
 &lt;style&gt;
 .fcv-wrap {
 background: transparent;
 color: #e0e0e0;
 font-family: "Segoe UI", sans-serif;
 margin: 1.5rem 0;
 }
 .fcv-wrap .fcv-header {
 margin-bottom: 12px;
 }
 .fcv-wrap .fcv-header h3 {
 color: #7eb8f7;
 font-size: 1rem;
 margin: 0 0 4px;
 border: none;
 }
 .fcv-wrap .fcv-header p {
 color: #888;
 font-size: 0.78rem;
 margin: 0;
 }
 .fcv-wrap .fcv-controls {
 display: flex;
 gap: 8px;
 flex-wrap: wrap;
 align-items: center;
 margin-bottom: 12px;
 padding: 10px 12px;
 background: #1a1d27;
 border-radius: 8px;
 }
 .fcv-wrap .fcv-ctrl-label {
 font-size: 0.75rem;
 color: #888;
 }
 .fcv-wrap .fcv-sep {
 width: 1px;
 height: 18px;
 background: #333;
 margin: 0 4px;
 }
 .fcv-wrap .fcv-freq-btn {
 background: #252836;
 color: #aaa;
 border: 1px solid #333;
 border-radius: 4px;
 padding: 3px 10px;
 font-size: 0.75rem;
 cursor: pointer;
 transition: all 0.15s;
 }
 .fcv-wrap .fcv-freq-btn:hover {
 background: #2e3347;
 color: #e0e0e0;
 }
 .fcv-wrap .fcv-freq-btn.active {
 background: #2a4a7f;
 color: #7eb8f7;
 border-color: #3a6abf;
 }
 .fcv-wrap .fcv-main {
 display: grid;
 grid-template-columns: 1fr 1fr;
 gap: 12px;
 }
 @media (max-width: 640px) {
 .fcv-wrap .fcv-main {
 grid-template-columns: 1fr;
 }
 }
 .fcv-wrap .fcv-panel {
 background: #1a1d27;
 border-radius: 8px;
 overflow: hidden;
 }
 .fcv-wrap .fcv-panel-title {
 font-size: 0.72rem;
 color: #7eb8f7;
 padding: 8px 12px 4px;
 display: flex;
 justify-content: space-between;
 align-items: center;
 gap: 4px;
 min-height: 2rem;
 }
 .fcv-wrap .fcv-val {
 font-size: 0.7rem;
 color: #f0a500;
 font-family: "JetBrains Mono", monospace;
 white-space: nowrap;
 flex-shrink: 0;
 min-width: 10rem;
 text-align: right;
 }
 .fcv-wrap canvas {
 width: 100%;
 height: 220px;
 display: block;
 }
 .fcv-wrap .fcv-info-bar {
 display: flex;
 gap: 8px;
 flex-wrap: nowrap;
 overflow: hidden;
 padding: 6px 12px;
 font-size: 0.68rem;
 color: #666;
 font-family: "JetBrains Mono", monospace;
 border-top: 1px solid #222;
 white-space: nowrap;
 }
 .fcv-wrap .fcv-info-bar span {
 flex-shrink: 1;
 overflow: hidden;
 text-overflow: ellipsis;
 }
 .fcv-wrap .fcv-info-bar b {
 color: #aaa;
 }
 .fcv-wrap .fcv-match-label {
 font-size: 0.7rem;
 }
 &lt;/style&gt;

 &lt;div class="fcv-header"&gt;
 &lt;h3&gt;푸리에 변환 — 복소평면 시각화&lt;/h3&gt;
 &lt;p&gt;
 h(t) = sin(2π·f_signal·t) &amp;nbsp;|&amp;nbsp; 검사 주파수 x 를 바꿔가며 적분값이
 어떻게 달라지는지 확인
 &lt;/p&gt;
 &lt;/div&gt;

 &lt;div class="fcv-controls"&gt;
 &lt;span class="fcv-ctrl-label"&gt;신호 주파수 f =&lt;/span&gt;
 &lt;button class="fcv-freq-btn active" data-fcv-signal="3"&gt;3 Hz&lt;/button&gt;
 &lt;button class="fcv-freq-btn" data-fcv-signal="5"&gt;5 Hz&lt;/button&gt;
 &lt;button class="fcv-freq-btn" data-fcv-signal="7"&gt;7 Hz&lt;/button&gt;
 &lt;div class="fcv-sep"&gt;&lt;/div&gt;
 &lt;span class="fcv-ctrl-label"&gt;검사 주파수 x =&lt;/span&gt;
 &lt;button class="fcv-freq-btn" data-fcv-check="1"&gt;1 Hz&lt;/button&gt;
 &lt;button class="fcv-freq-btn" data-fcv-check="2"&gt;2 Hz&lt;/button&gt;
 &lt;button class="fcv-freq-btn active" data-fcv-check="3"&gt;3 Hz ✓&lt;/button&gt;
 &lt;button class="fcv-freq-btn" data-fcv-check="5"&gt;5 Hz&lt;/button&gt;
 &lt;button class="fcv-freq-btn" data-fcv-check="7"&gt;7 Hz&lt;/button&gt;
 &lt;div class="fcv-sep"&gt;&lt;/div&gt;
 &lt;span class="fcv-ctrl-label"&gt;속도&lt;/span&gt;
 &lt;button class="fcv-freq-btn" data-fcv-speed="0.3"&gt;0.3×&lt;/button&gt;
 &lt;button class="fcv-freq-btn active" data-fcv-speed="1"&gt;1×&lt;/button&gt;
 &lt;button class="fcv-freq-btn" data-fcv-speed="2"&gt;2×&lt;/button&gt;
 &lt;/div&gt;

 &lt;div class="fcv-main"&gt;
 &lt;div class="fcv-panel"&gt;
 &lt;div class="fcv-panel-title"&gt;
 복소평면 — h(t)·e^{−2πitx} 궤적
 &lt;span class="fcv-val" id="fcv-sumVal"&gt;합: —&lt;/span&gt;
 &lt;/div&gt;
 &lt;canvas id="fcv-complexCanvas"&gt;&lt;/canvas&gt;
 &lt;div class="fcv-info-bar"&gt;
 &lt;span&gt;t = &lt;b id="fcv-tVal"&gt;0.000&lt;/b&gt;s&lt;/span&gt;
 &lt;span
 &gt;Re=&lt;b id="fcv-reVal"&gt;0.000&lt;/b&gt; Im=&lt;b id="fcv-imVal"&gt;0.000&lt;/b&gt;&lt;/span
 &gt;
 &lt;/div&gt;
 &lt;/div&gt;

 &lt;div class="fcv-panel"&gt;
 &lt;div class="fcv-panel-title"&gt;
 시간 도메인 — h(t) 와 검사파
 &lt;span class="fcv-match-label" id="fcv-matchLabel"&gt;&lt;/span&gt;
 &lt;/div&gt;
 &lt;canvas id="fcv-timeCanvas"&gt;&lt;/canvas&gt;
 &lt;div class="fcv-info-bar"&gt;
 &lt;span&gt;누적 Re = &lt;b id="fcv-cumRe"&gt;0.000&lt;/b&gt;&lt;/span&gt;
 &lt;span&gt;누적 Im = &lt;b id="fcv-cumIm"&gt;0.000&lt;/b&gt;&lt;/span&gt;
 &lt;span&gt;|합| = &lt;b id="fcv-cumMag"&gt;0.000&lt;/b&gt;&lt;/span&gt;
 &lt;/div&gt;
 &lt;/div&gt;
 &lt;/div&gt;

 &lt;script&gt;
 (function () {
 const state = {
 fSignal: 3,
 fCheck: 3,
 speed: 1,
 t: 0,
 trailRe: [],
 trailIm: [],
 cumRe: 0,
 cumIm: 0,
 dt: 0.005,
 maxTrail: 600,
 };

 const wrap = document.currentScript.closest(".fcv-wrap");

 wrap.querySelectorAll("[data-fcv-signal]").forEach((btn) =&gt; {
 btn.addEventListener("click", () =&gt; {
 wrap
 .querySelectorAll("[data-fcv-signal]")
 .forEach((b) =&gt; b.classList.remove("active"));
 btn.classList.add("active");
 state.fSignal = parseFloat(btn.dataset.fcvSignal);
 reset();
 updateMatchLabel();
 });
 });
 wrap.querySelectorAll("[data-fcv-check]").forEach((btn) =&gt; {
 btn.addEventListener("click", () =&gt; {
 wrap
 .querySelectorAll("[data-fcv-check]")
 .forEach((b) =&gt; b.classList.remove("active"));
 btn.classList.add("active");
 state.fCheck = parseFloat(btn.dataset.fcvCheck);
 reset();
 updateMatchLabel();
 });
 });
 wrap.querySelectorAll("[data-fcv-speed]").forEach((btn) =&gt; {
 btn.addEventListener("click", () =&gt; {
 wrap
 .querySelectorAll("[data-fcv-speed]")
 .forEach((b) =&gt; b.classList.remove("active"));
 btn.classList.add("active");
 state.speed = parseFloat(btn.dataset.fcvSpeed);
 });
 });

 function reset() {
 state.t = 0;
 state.trailRe = [];
 state.trailIm = [];
 state.cumRe = 0;
 state.cumIm = 0;
 }

 function updateMatchLabel() {
 const el = wrap.querySelector("#fcv-matchLabel");
 if (state.fSignal === state.fCheck) {
 el.textContent = "✓ 주파수 일치 → 발산";
 el.style.color = "#4aff8a";
 } else {
 el.textContent = "✗ 주파수 불일치 → 상쇄";
 el.style.color = "#ff6b6b";
 }
 }
 updateMatchLabel();

 const cC = wrap.querySelector("#fcv-complexCanvas");
 const cT = wrap.querySelector("#fcv-timeCanvas");

 function resize() {
 const dpr = window.devicePixelRatio || 1;
 [cC, cT].forEach((c) =&gt; {
 const rect = c.getBoundingClientRect();
 c.width = rect.width * dpr;
 c.height = 220 * dpr;
 });
 }
 window.addEventListener("resize", resize);
 resize();

 function drawComplex() {
 const dpr = window.devicePixelRatio || 1;
 const ctx = cC.getContext("2d");
 const W = cC.width / dpr,
 H = cC.height / dpr;
 ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
 ctx.clearRect(0, 0, W, H);

 const cx = W / 2,
 cy = H / 2;
 const R = Math.min(W, H) * 0.38;

 ctx.beginPath();
 ctx.arc(cx, cy, R, 0, Math.PI * 2);
 ctx.strokeStyle = "rgba(30,60,100,0.4)";
 ctx.lineWidth = 1;
 ctx.stroke();

 ctx.strokeStyle = "rgba(255,255,255,0.07)";
 ctx.lineWidth = 1;
 ctx.beginPath();
 ctx.moveTo(cx - R - 20, cy);
 ctx.lineTo(cx + R + 20, cy);
 ctx.stroke();
 ctx.beginPath();
 ctx.moveTo(cx, cy - R - 20);
 ctx.lineTo(cx, cy + R + 20);
 ctx.stroke();

 ctx.font = "9px JetBrains Mono, monospace";
 ctx.fillStyle = "rgba(74,158,255,0.35)";
 ctx.fillText("Re", cx + R + 6, cy + 4);
 ctx.fillText("Im", cx + 4, cy - R - 8);
 ctx.fillText("+1", cx + R - 14, cy - 6);
 ctx.fillText("-1", cx - R - 2, cy - 6);

 if (state.trailRe.length &gt; 1) {
 ctx.beginPath();
 for (let i = 0; i &lt; state.trailRe.length; i++) {
 const px = cx + state.trailRe[i] * R;
 const py = cy - state.trailIm[i] * R;
 if (i === 0) ctx.moveTo(px, py);
 else ctx.lineTo(px, py);
 }
 ctx.strokeStyle =
 state.fSignal === state.fCheck
 ? "rgba(74,255,140,0.5)"
 : "rgba(255,107,107,0.4)";
 ctx.lineWidth = 1.5;
 ctx.stroke();
 }

 const h = Math.sin(2 * Math.PI * state.fSignal * state.t);
 const angle = -2 * Math.PI * state.fCheck * state.t;
 const re = h * Math.cos(angle);
 const im = h * Math.sin(angle);
 const px = cx + re * R,
 py = cy - im * R;

 ctx.beginPath();
 ctx.moveTo(cx, cy);
 ctx.lineTo(px, py);
 ctx.strokeStyle = "rgba(255,255,255,0.15)";
 ctx.lineWidth = 1;
 ctx.stroke();

 ctx.beginPath();
 ctx.arc(px, py, 5, 0, Math.PI * 2);
 ctx.fillStyle = state.fSignal === state.fCheck ? "#4aff8a" : "#ff6b6b";
 ctx.fill();

 const cumScale = 1 / (state.trailRe.length + 1);
 const avgRe = state.cumRe * cumScale;
 const avgIm = state.cumIm * cumScale;
 const sumPx = cx + avgRe * R,
 sumPy = cy - avgIm * R;

 ctx.beginPath();
 ctx.moveTo(cx, cy);
 ctx.lineTo(sumPx, sumPy);
 ctx.strokeStyle = "#f0a500";
 ctx.lineWidth = 2;
 ctx.stroke();
 ctx.beginPath();
 ctx.arc(sumPx, sumPy, 4, 0, Math.PI * 2);
 ctx.fillStyle = "#f0a500";
 ctx.fill();

 const mag = Math.sqrt(avgRe * avgRe + avgIm * avgIm);
 wrap.querySelector("#fcv-sumVal").textContent =
 `|평균| = ${mag.toFixed(3)}`;
 }

 function drawTime() {
 const dpr = window.devicePixelRatio || 1;
 const ctx = cT.getContext("2d");
 const W = cT.width / dpr,
 H = cT.height / dpr;
 ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
 ctx.clearRect(0, 0, W, H);

 const midH = H / 2,
 amp = H * 0.22,
 duration = 2;

 ctx.strokeStyle = "rgba(255,255,255,0.07)";
 ctx.lineWidth = 1;
 ctx.beginPath();
 ctx.moveTo(0, midH);
 ctx.lineTo(W, midH);
 ctx.stroke();

 ctx.beginPath();
 for (let px = 0; px &lt; W; px++) {
 const t = (px / W) * duration;
 const y = midH - Math.sin(2 * Math.PI * state.fSignal * t) * amp;
 if (px === 0) ctx.moveTo(px, y);
 else ctx.lineTo(px, y);
 }
 ctx.strokeStyle = "rgba(74,158,255,0.7)";
 ctx.lineWidth = 1.5;
 ctx.stroke();

 ctx.beginPath();
 for (let px = 0; px &lt; W; px++) {
 const t = (px / W) * duration;
 const y = midH - Math.cos(2 * Math.PI * state.fCheck * t) * amp;
 if (px === 0) ctx.moveTo(px, y);
 else ctx.lineTo(px, y);
 }
 ctx.strokeStyle = "rgba(240,165,0,0.5)";
 ctx.lineWidth = 1.5;
 ctx.stroke();

 ctx.beginPath();
 const tStart = (state.t / duration) * W;
 for (let px = 0; px &lt; Math.min(tStart, W); px++) {
 const t = (px / W) * duration;
 const product =
 Math.sin(2 * Math.PI * state.fSignal * t) *
 Math.cos(2 * Math.PI * state.fCheck * t);
 const y = midH - product * amp;
 if (px === 0) ctx.moveTo(px, midH);
 ctx.lineTo(px, y);
 }
 ctx.lineTo(Math.min(tStart, W), midH);
 ctx.closePath();
 ctx.fillStyle =
 state.fSignal === state.fCheck
 ? "rgba(74,255,140,0.12)"
 : "rgba(255,107,107,0.1)";
 ctx.fill();

 const curX = (state.t / duration) * W;
 if (curX &lt;= W) {
 ctx.beginPath();
 ctx.moveTo(curX, 0);
 ctx.lineTo(curX, H);
 ctx.strokeStyle = "rgba(255,255,255,0.2)";
 ctx.lineWidth = 1;
 ctx.setLineDash([3, 4]);
 ctx.stroke();
 ctx.setLineDash([]);
 }

 ctx.font = "9px JetBrains Mono, monospace";
 ctx.fillStyle = "rgba(74,158,255,0.6)";
 ctx.fillText(`h(t) = sin(2π·${state.fSignal}·t)`, 8, 16);
 ctx.fillStyle = "rgba(240,165,0,0.6)";
 ctx.fillText(`검사파 = cos(2π·${state.fCheck}·t)`, 8, 28);
 }

 let last = null;
 function loop(ts) {
 if (!last) last = ts;
 const elapsed = (ts - last) / 1000;
 last = ts;

 state.t += elapsed * state.speed;
 if (state.t &gt; 2) {
 state.t = 0;
 state.trailRe = [];
 state.trailIm = [];
 state.cumRe = 0;
 state.cumIm = 0;
 }

 const h = Math.sin(2 * Math.PI * state.fSignal * state.t);
 const angle = -2 * Math.PI * state.fCheck * state.t;
 const re = h * Math.cos(angle);
 const im = h * Math.sin(angle);

 state.trailRe.push(re);
 state.trailIm.push(im);
 if (state.trailRe.length &gt; state.maxTrail) {
 state.trailRe.shift();
 state.trailIm.shift();
 }
 state.cumRe += re * state.dt;
 state.cumIm += im * state.dt;

 drawComplex();
 drawTime();

 wrap.querySelector("#fcv-tVal").textContent = state.t.toFixed(3);
 wrap.querySelector("#fcv-reVal").textContent = re.toFixed(3);
 wrap.querySelector("#fcv-imVal").textContent = im.toFixed(3);
 wrap.querySelector("#fcv-cumRe").textContent = state.cumRe.toFixed(3);
 wrap.querySelector("#fcv-cumIm").textContent = state.cumIm.toFixed(3);
 wrap.querySelector("#fcv-cumMag").textContent = Math.sqrt(
 state.cumRe ** 2 + state.cumIm ** 2,
 ).toFixed(3);

 requestAnimationFrame(loop);
 }

 requestAnimationFrame(loop);
 })();
 &lt;/script&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="이산-푸리에-변환-dft"&gt;&lt;a href="#%ec%9d%b4%ec%82%b0-%ed%91%b8%eb%a6%ac%ec%97%90-%eb%b3%80%ed%99%98-dft" class="header-anchor"&gt;&lt;/a&gt;이산 푸리에 변환 (DFT)
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;현실에서 얻을 수 있는 데이터는 이산적이다.
&lt;ul&gt;
&lt;li&gt;물론 라플라스 변환마냥 수학적으로 변환해서 계산하는 테크닉적으로 쓰려면 그냥 쓰면 된다.&lt;/li&gt;
&lt;li&gt;사인파를 많이 쓸거같은 음악 데이터로 생각해보면, 결국 소리를 받을때도, 저장할때도 이산적으로 저장할 수 밖에 없다.&lt;/li&gt;
&lt;li&gt;따라서 이산 데이터에 대해서도 푸리에 변환을 정의해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그러면 적분값 대신 시그마 값으로 표현할 수 있겠다.&lt;/li&gt;
&lt;li&gt;
$$X[k] = \sum_{n=0}^{N-1}x[n]\exp\left(-i\frac{2\pi kn}{N}\right) = \sum_{n = 0}^{N - 1}{x[n] \omega^{kn}} \,\,\,\,  (\omega = \exp\left(-i\dfrac{2\pi}{N}\right))$$&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;연속&lt;/th&gt;
 &lt;th&gt;이산&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;$t$&lt;/td&gt;
 &lt;td&gt;$n/N$&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;$h(t)$&lt;/td&gt;
 &lt;td&gt;$x[n]$&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;$x$ (주파수)&lt;/td&gt;
 &lt;td&gt;$k$&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;$dt$&lt;/td&gt;
 &lt;td&gt;$1/N$&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;$e^{-2\pi i t x}$&lt;/td&gt;
 &lt;td&gt;$\omega^{kn}$&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;위와 같이 대응되게 생각하면 된다.
&lt;ul&gt;
&lt;li&gt;$\omega$는 단위근이라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이때 $\omega^N = 1$이므로, $0 \leq k &lt; N$ 인 $k$에 대해서만 유의미한 결과를 얻어낼 수 있다.
&lt;ul&gt;
&lt;li&gt;따라서 DFT에서는 최대 $N$개의 주파수에 대해서만 검증할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;여기서도 물론 역변환을 정의할 수 있다.

$$x[n] = \frac{1}{N}\sum_{k=0}^{N-1}X[k]\omega^{-kn}$$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="고속-푸리에-변환"&gt;&lt;a href="#%ea%b3%a0%ec%86%8d-%ed%91%b8%eb%a6%ac%ec%97%90-%eb%b3%80%ed%99%98" class="header-anchor"&gt;&lt;/a&gt;고속 푸리에 변환
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;위의 푸리에 변환을 계산하는데, 총 $N$개의 주파수에 대해 $N$개 신호의합을 구하니까 $O(N^2)$의 시간복잡도가 걸린다.&lt;/li&gt;
&lt;li&gt;여기서 관찰을 조금 해보자.
&lt;ul&gt;
&lt;li&gt;8개의 데이터 $x_0, x_1, x_2, \cdots, x_7$이 있다고 해보자.&lt;/li&gt;
&lt;li&gt;이때 $x_n, x_{n+4}$에 곱해지는 계수를 비교하면 $\exp{(-i\pi k)}$ 만큼의 차이가 난다.&lt;/li&gt;
&lt;li&gt;$k$가 홀수일때는 $-1$, 짝수일때는 $1$이다.&lt;/li&gt;
&lt;li&gt;이를 이용해서 DFT의 식을 짝수항과 홀수항으로 나눠서 다음과 같이 쓸 수 있다.
 
$$\begin{align}
 X[k] &amp;= \sum_{n=0}^{N-1} x[n]\exp\left(-i\frac{2\pi kn}{N}\right) \\
 &amp;= \sum_{n=0}^{N/2-1} x[2n]\exp\left(-i\frac{2\pi k(2n)}{N}\right) + \sum_{n=0}^{N/2-1} x[2n+1]\exp\left(-i\frac{2\pi k(2n+1)}{N}\right) \\
 &amp;= \sum_{n=0}^{N/2-1} x[2n]\exp\left(-i\frac{2\pi kn}{N/2}\right) + \exp\left(-i\frac{2\pi k}{N}\right)\sum_{n=0}^{N/2-1} x[2n+1]\exp\left(-i\frac{2\pi kn}{N/2}\right) \\
 &amp;= E[k] + \exp\left(-i\frac{2\pi k}{N}\right)O[k] \\
 &amp;= E[k] + \omega^kO[k]
 \end{align}$$
 - 관찰
 - 위 식의 짝수항 / 홀수항 부분은 $N/2$ 크기의 DFT와 같다.
 - 이 식은 $k &lt; N/2$ 에 대해서만 정의되지만, $k \geq N/2$에 대해서 처리하는 방법은 위에서 찾았다.

$$\begin{align}
X[k+N/2] &amp;= \sum_{n=0}^{N-1} x[n]\exp\left(-i\frac{2\pi (k+N/2)n}{N}\right) \\
&amp;= \sum_{n=0}^{N-1} x[n]\exp\left(-i\frac{2\pi kn}{N}\right)\exp(-i\pi n) \\
&amp;= E[k] - \omega^k O[k]
\end{align}$$
 - 따라서 분할정복을 이용해서 $O(N\log{N})$ 시간에 연산을 수행할 수 있겠다.
 - 이를 고속 푸리에 변환, FFT라 하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="합성곱"&gt;&lt;a href="#%ed%95%a9%ec%84%b1%ea%b3%b1" class="header-anchor"&gt;&lt;/a&gt;합성곱
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;근데 저 수학 씹덕같은걸 왜배웠냐?
&lt;ul&gt;
&lt;li&gt;DFT를 통해 합성곱을, 합성곱으로 다항식의 곱셈을, 다항식의 곱셈으로 두 수의 곱셈을 할 수 있다고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;다음과 같은 함수가 있다고 하자.
&lt;ul&gt;
&lt;li&gt;$f(x) = a_{N-1}x^{N-1} + a_{N-2}x^{N-2} + \cdots + a_1x + a_0$&lt;/li&gt;
&lt;li&gt;여기서, $x = \omega^k$를 대입하면 그 결과는 DFT와 같다!&lt;/li&gt;
&lt;li&gt;이를 이용해서 두 다항식 $f, g$에 대한 DFT로 새로운 함수 $h$에 대한 DFT 결과값을 구하고, 이를 역변환해서 $h$를 찾아낼 수 있겠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이걸 코드로 옮기면 다음과 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;using&lt;/span&gt; cd &lt;span style="color:#f92672"&gt;=&lt;/span&gt; complex&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;double&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; PI &lt;span style="color:#f92672"&gt;=&lt;/span&gt; acos(&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; fft(vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; a){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; N &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a.size();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(N &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; a;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; even(N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;), odd(N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; even[i] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a[&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;i];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; odd[i] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a[&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; even &lt;span style="color:#f92672"&gt;=&lt;/span&gt; fft(even);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; odd &lt;span style="color:#f92672"&gt;=&lt;/span&gt; fft(odd);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; ret(N);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(k, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cd t &lt;span style="color:#f92672"&gt;=&lt;/span&gt; polar(&lt;span style="color:#ae81ff"&gt;1.0&lt;/span&gt;, &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;PI&lt;span style="color:#f92672"&gt;*&lt;/span&gt;k&lt;span style="color:#f92672"&gt;/&lt;/span&gt;N) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; odd[k];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ret[k] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; even[k] &lt;span style="color:#f92672"&gt;+&lt;/span&gt; t;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ret[k&lt;span style="color:#f92672"&gt;+&lt;/span&gt;N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; even[k] &lt;span style="color:#f92672"&gt;-&lt;/span&gt; t;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; ret;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; ifft(vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; a){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; N &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a.size();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(N &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; a;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; even(N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;), odd(N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; even[i] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a[&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;i];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; odd[i] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a[&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; even &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ifft(even);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; odd &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ifft(odd);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; ret(N);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(k, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cd t &lt;span style="color:#f92672"&gt;=&lt;/span&gt; polar(&lt;span style="color:#ae81ff"&gt;1.0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;PI&lt;span style="color:#f92672"&gt;*&lt;/span&gt;k&lt;span style="color:#f92672"&gt;/&lt;/span&gt;N) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; odd[k];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ret[k] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; even[k] &lt;span style="color:#f92672"&gt;+&lt;/span&gt; t;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ret[k&lt;span style="color:#f92672"&gt;+&lt;/span&gt;N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; even[k] &lt;span style="color:#f92672"&gt;-&lt;/span&gt; t;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; ret;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;ifft를 사용할때 마지막에 a의 길이로 나눠줘야 함에 주의하자.&lt;/li&gt;
&lt;li&gt;사실상 구조가 똑같아서, bool inv같은걸로 최적화하는게 더 좋은거같다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="최적화"&gt;&lt;a href="#%ec%b5%9c%ec%a0%81%ed%99%94" class="header-anchor"&gt;&lt;/a&gt;최적화
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;하지만 이렇게 재귀함수로 새로 배열을 만들면서 진행하면, 느리고 메모리도 많이 먹는다.&lt;/li&gt;
&lt;li&gt;인접한 원소들 끼리만 연산할 수 있게, 배열을 재배치하자.&lt;/li&gt;
&lt;li&gt;$[0, 4, 2, 6, 1, 5, 3, 7]$과 같은 모양이면 좋겠다.&lt;/li&gt;
&lt;li&gt;이는 비트를 좌우로 뒤집는 것으로 수행 가능하다.&lt;/li&gt;
&lt;li&gt;이렇게 하면 반복문으로 계산할 수 있고, polar 극좌표점 자체도 미리 계산해둬서 쓰면 $NlogN$번에서 $N/2$번으로 줄일 수 있겠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;namespace&lt;/span&gt; FFT{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fft&lt;/span&gt;(vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;a, &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; inv &lt;span style="color:#f92672"&gt;=&lt;/span&gt; false){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; N &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a.size();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assert(N &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; (N &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; (N&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// N은 2의 거듭제곱이어야 함
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 비트 뒤집기
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, j &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; N; i&lt;span style="color:#f92672"&gt;++&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; bit &lt;span style="color:#f92672"&gt;=&lt;/span&gt; N &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(; j &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; bit; bit &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) j &lt;span style="color:#f92672"&gt;^=&lt;/span&gt; bit;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; j &lt;span style="color:#f92672"&gt;^=&lt;/span&gt; bit;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; j) swap(a[i], a[j]);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 단위근 미리 계산
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; ang &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; acos(&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; N &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (inv &lt;span style="color:#f92672"&gt;?&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; w(N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// 단위근
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;) w[i] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; cd(cos(ang&lt;span style="color:#f92672"&gt;*&lt;/span&gt;i), sin(ang&lt;span style="color:#f92672"&gt;*&lt;/span&gt;i));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Cooley-Tukey FFT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; len &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;; len &lt;span style="color:#f92672"&gt;&amp;lt;=&lt;/span&gt; N; len &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; step &lt;span style="color:#f92672"&gt;=&lt;/span&gt; N &lt;span style="color:#f92672"&gt;/&lt;/span&gt; len;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; N; i &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; len){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(j, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, len&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cd even &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a[i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;j], odd &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a[i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;j&lt;span style="color:#f92672"&gt;+&lt;/span&gt;(len&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)] &lt;span style="color:#f92672"&gt;*&lt;/span&gt; w[j&lt;span style="color:#f92672"&gt;*&lt;/span&gt;step];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; a[i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;j] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; even &lt;span style="color:#f92672"&gt;+&lt;/span&gt; odd;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; a[i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;j&lt;span style="color:#f92672"&gt;+&lt;/span&gt;(len&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; even &lt;span style="color:#f92672"&gt;-&lt;/span&gt; odd;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 역 FFT인 경우 결과를 N으로 나눔
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(inv) rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N) a[i] &lt;span style="color:#f92672"&gt;/=&lt;/span&gt; N;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;1067번 문제 기준 608ms -&amp;gt; 92ms로, 6.6배 가까이 줄어들었다!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="질문-사항"&gt;&lt;a href="#%ec%a7%88%eb%ac%b8-%ec%82%ac%ed%95%ad" class="header-anchor"&gt;&lt;/a&gt;❔질문 사항
&lt;/h2&gt;&lt;blockquote class="alert alert-question"&gt;
 &lt;div class="alert-header"&gt;
 &lt;span class="alert-icon"&gt;&lt;/span&gt;
 &lt;span class="alert-title"&gt;주파수에 $2\pi$를 넣는 이유&lt;/span&gt;
 &lt;/div&gt;
 &lt;div class="alert-body"&gt;
 &lt;p&gt;주파수 $f$ -&amp;gt; 1초에 몇번 진동하는가
그냥 $\sin{ft}$라고 쓰면 $t = 1$일때 까지 $f$번 진동하지 않는다 (주기가 $2\pi$니까)
따라서 $2\pi$를 곱해줘서 각주파수로 변환해준다.&lt;/p&gt;
 &lt;/div&gt;
 &lt;/blockquote&gt;
&lt;blockquote class="alert alert-question"&gt;
 &lt;div class="alert-header"&gt;
 &lt;span class="alert-icon"&gt;&lt;/span&gt;
 &lt;span class="alert-title"&gt;$h(x)$의 모든 값들은 발산하는데, 어떻게 그리지?&lt;/span&gt;
 &lt;/div&gt;
 &lt;div class="alert-body"&gt;
 &lt;p&gt;그래서 수학적으로는 디렉 델타 함수를 이용한다.
하지만 현실 데이터에서는 신호가 유한하거나 / 감쇠하기때문에, 변환 결과 발산하지 않고 자연스럽게 수렴한다.&lt;/p&gt;
 &lt;/div&gt;
 &lt;/blockquote&gt;
&lt;h2 id="-참고-자료"&gt;&lt;a href="#-%ec%b0%b8%ea%b3%a0-%ec%9e%90%eb%a3%8c" class="header-anchor"&gt;&lt;/a&gt;🔗 참고 자료
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.acmicpc.net/blog/view/141" target="_blank" rel="noopener"
 &gt;DFT, FFT 이해하기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>