<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>thisisuser's study</title>
    <link>https://thisisuser.tistory.com/</link>
    <description>공부할 거 이것저것</description>
    <language>ko</language>
    <pubDate>Sat, 20 Jun 2026 10:43:16 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>thisisuserr</managingEditor>
    <image>
      <title>thisisuser's study</title>
      <url>https://tistory1.daumcdn.net/tistory/6858950/attach/f188980292394bd2af3112dedb304617</url>
      <link>https://thisisuser.tistory.com</link>
    </image>
    <item>
      <title>BOJ 11011. Forged Answer</title>
      <link>https://thisisuser.tistory.com/82</link>
      <description>&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11011&quot;&gt;11011번: Forged Answers (acmicpc.net)&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;solved.ac:&lt;span&gt; &lt;/span&gt;&lt;span style=&quot;color: #ec9a00;&quot;&gt;Gold I&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(2024.09.13)&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아이디어 하나가 필요한 그리디 문제입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문제를 요약하면, 답지를 조작해서 세 사람의 점수 중 가장 낮은 값을 최대로 끌어올리는 문제입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음과 같이 각 문제를 분류할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;세 사람 다 동일한 답안을 낸 경우&lt;/li&gt;
&lt;li&gt;두 사람이 동일한 답안을 내고, 한 사람은 다른 답안을 낸 경우&lt;/li&gt;
&lt;li&gt;세 사람 모두 다른 답안을 낸 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번의 경우, 당연히 다 맞게 하는 게 최적입니다. 이는 너무나 자명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번의 경우 원하는 사람의 점수를 1 올릴 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번의 경우가 조금 특이합니다. 여기서는 두 사람의 점수를 1 올리거나, 한 사람의 점수를 1 올릴 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 솔직히 좀 난해합니다. 다음과 같이 그림을 그리겠습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyqSbq/btsJAjoWjW6/xP8n1y80H83s0WziqwKOBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyqSbq/btsJAjoWjW6/xP8n1y80H83s0WziqwKOBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyqSbq/btsJAjoWjW6/xP8n1y80H83s0WziqwKOBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdyqSbq%2FbtsJAjoWjW6%2FxP8n1y80H83s0WziqwKOBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;100&quot; height=&quot;197&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 3번 문제는 고려하지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연보라색은 1번의 경우, 나머지는 2번의 경우이며 같은 색깔은 두 학생이 같은 제출을 내었음을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 모두 두 명이 점수를 얻도록 구성하였습니다. 높이는 점수와 비례하며, 같은 색깔 블록의 높이는 동일해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 항상 저런 꼴이 나올 수 있는 이유는, 2번 문제의 경우 학생을 고려하면 3가지 경우가 나옵니다. (1, 2), (1, 3), (2, 3)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 문제의 개수를 a, b, c라 하면 학생을 바꿈으로써 a &amp;gt;= b &amp;gt;= c를 만들 수 있고, a + b &amp;gt;= a + c &amp;gt;= b + c이므로 항상 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Without Loss Of Generality)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 약간의 케이스워크를 해 봅시다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 3번 문제가 충분함&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;811&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D2ExD/btsJBQlhjXN/T6vxRQyJwWQL2CdeuQ0ot1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D2ExD/btsJBQlhjXN/T6vxRQyJwWQL2CdeuQ0ot1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D2ExD/btsJBQlhjXN/T6vxRQyJwWQL2CdeuQ0ot1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD2ExD%2FbtsJBQlhjXN%2FT6vxRQyJwWQL2CdeuQ0ot1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;100&quot; height=&quot;223&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;811&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진한 보라색이 3번 문제입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, 당연하게도 낮은 사람 먼저 채워주고, 나머지는 3등분하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최댓값은 그냥 싹 더해서 3으로 나누어 주면 됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 약간 부족함&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xzWBX/btsJASK09YD/xAPfMcKUwaFxcs7wZ2foXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xzWBX/btsJASK09YD/xAPfMcKUwaFxcs7wZ2foXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xzWBX/btsJASK09YD/xAPfMcKUwaFxcs7wZ2foXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxzWBX%2FbtsJASK09YD%2FxAPfMcKUwaFxcs7wZ2foXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;100&quot; height=&quot;197&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번 문제에서 같은 색깔 블록 하나씩 뺀 다음에 나머지를 추가할 수는 있겠습니다만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 블록을 빼던 낮은 점수를 가진 학생의 점수가 1 감소합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 하나를 빼서 할당할 수 있는 점수는 고작 1이기 때문에, 의미없는 행동입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 그냥 점수가 낮은 두 학생에 골고루 채워주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 최댓값은 (1번 막대 + 2번 막대 + 3번 문제 수) / 2입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 많이 부족함&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ekhert/btsJAOoszr0/Yo1tKy50lsAOp6riTnib8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ekhert/btsJAOoszr0/Yo1tKy50lsAOp6riTnib8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ekhert/btsJAOoszr0/Yo1tKy50lsAOp6riTnib8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fekhert%2FbtsJAOoszr0%2FYo1tKy50lsAOp6riTnib8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;100&quot; height=&quot;197&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, 빨간색 블록의 높이를 줄인 만큼 가장 왼쪽의 막대를 늘릴 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상 3번째 블록이 2번째 블록보단 높게 되므로, 잠시 무시하고 1번째와 2번째 막대만 관찰해 봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번 막대가 1 내려가면, 1번 막대가 1 올라가게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 적당히 잘 맞췄다고 가정하면, 이후로는 2번의 케이스가 됩니다. 더 이상 진행하지 않아도 문제 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 경우 (1번 막대 + 2번 막대 + 3번 문제 수) / 2가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 구현 좀 해 주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여담으로 2번 맞왜틀을 했는데, else if 까먹어서 틀린 거였습니다, 이런.&lt;/p&gt;
&lt;pre id=&quot;code_1726196286176&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int solve()
{
  int n;
  string arr[3];

  cin &amp;gt;&amp;gt; n;
  for(int i = 0; i &amp;lt; 3; i++) cin &amp;gt;&amp;gt; arr[i];

  int a = 0, b[3] = {0, 0, 0}, c = 0;
  for(int i = 0; i &amp;lt; n; i++)
    {
      if(arr[0][i] == arr[1][i] &amp;amp;&amp;amp; arr[1][i] == arr[2][i]) a++;
      else
      {
        if(arr[0][i] == arr[1][i]) b[0]++;
        else if(arr[1][i] == arr[2][i]) b[1]++;
        else if(arr[2][i] == arr[0][i]) b[2]++;
        else c++;
      }
    }

  int res[3] = {a, a, a};
  for(int i = 0; i &amp;lt; 3; i++)
    {
      res[i] += b[i];
      res[(i + 1) % 3] += b[i];
    }
  sort(res, res + 3);

  if(res[0] + res[1] + c &amp;gt;= res[2] * 2)
  {
    int rem = c - (res[2] - res[1]) - (res[2] - res[0]);
    return res[2] + rem / 3;
  }
  else return (res[0] + res[1] + c) / 2;
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0);

  int T;
  cin &amp;gt;&amp;gt; T;
  while(T--)
    {
      cout &amp;lt;&amp;lt; solve() &amp;lt;&amp;lt; &quot;\n&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Problem Solving</category>
      <author>thisisuserr</author>
      <guid isPermaLink="true">https://thisisuser.tistory.com/82</guid>
      <comments>https://thisisuser.tistory.com/82#entry82comment</comments>
      <pubDate>Fri, 13 Sep 2024 11:58:10 +0900</pubDate>
    </item>
    <item>
      <title>BOJ 13576. Prefix와 Suffix</title>
      <link>https://thisisuser.tistory.com/81</link>
      <description>&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/13576&quot;&gt;13576번: Prefix와 Suffix (acmicpc.net)&lt;/a&gt; &lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;solved.ac:&lt;span&gt; &lt;/span&gt;&lt;span style=&quot;color: #27e2a4;&quot;&gt;Platinum II&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(2024.09.10)&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;꽤나 흥미롭게 풀었던 문제입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;배울 점이 많은 문제라고 생각합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1. Prefix이자 Suffix인 substr 구하기&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음엔 전체 문자열 하나 잡고, 앞으로 계속 lcp랑 len이랑 다를 때까지 탐색하면 되지 않을 까 생각했습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러나 BBAABB같은 문자열을 생각해 보면, 씨알도 안 먹히는 소리임을 알 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음엔 쌩 SA + LCP만 가지고 풀어보려 했으나, 능지 이슈로 이건 아직도 모르겠습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 그냥 실패함수 가져다 썼습니다. 조건을 만족시키는 substr의 길이는 fail(len)을 반복적으로 수행하여 얻을 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2. 그래서 문자열 나타는 거 어케 확인함?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어떤 문자열에 대해서, 문자열이 나타나는 지의 여부를 판단하는 방법을 생각해 봅시다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문자열 S에 대해 S[i]를 i번째 글자부터 시작하는 접미사라 생각해 보면,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모든 접미사에 대해 해당 문자열이 접두사가 되는 지 확인하면 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;물론 나이브하게 구현하면 당연히 노답임을 알 수 있습니다. 우리가 접두사인지 판단해 보는 문자열도 접미사임을 상기합시다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;확인하고자 하는 접미사 T와 다른 접미사에 대해 공통 접두사의 길이가 len(T)라면, 그 접미사는 T를 접두사로 갖습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이때 T를 접두사로 갖는 접미사 A와 다른 접미사 B에 대해, 공통 접두사의 길이가 len(T) 이상이라면,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;B 역시 T를 접두사로 갖는 접미사임을 알 수 있게 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기까지 왔다면 뭘 써야 하는지는 지레짐작이 갑니다. 접미사 배열을 만들어야 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;접미사 배열은 정렬되어 있으므로, 인접한 두 접미사끼리 LCP를 비교하면서 한 번이라도 길이가 len(T) 미만으로 간다?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그 이후의 문자열은 볼 필요도 없습니다. 딱 거기까지만 개수를 세면 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;map 등을 이용해서 길이를 통해 인덱스를 O(logN) 미만으로 구할 수 있게 구성하면,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;N이 10만으로 상당히 애매하기 때문에 O(N&amp;sup2;)이 어떻게 잘 통과됩니다. 2초라서 7억 번 정도는 잘 돌아가는 것 같습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래는 첫 풀이입니다. (1540ms)&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1725938962428&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;map&amp;gt;

using namespace std;

/* returns pair of suffix array, lcp array. lcp range = [0, n - 1] */
pair&amp;lt;vector&amp;lt;int&amp;gt;, vector&amp;lt;int&amp;gt;&amp;gt; sa_lcp(string str)
{
  int n = str.length(), d = 1;
  vector&amp;lt;int&amp;gt; pos(n), sa(n), lcp(n - 1), tmp(n);

  auto cmp = [&amp;amp;n, &amp;amp;d, &amp;amp;pos](int i, int j)
  {
    if(pos[i] != pos[j]) return pos[i] &amp;lt; pos[j];
    i += d; j += d;
    return (i &amp;lt; n &amp;amp;&amp;amp; j &amp;lt; n) ? (pos[i] &amp;lt; pos[j]) : (i &amp;gt; j);
  };

  //struct suffix array
  for(int i = 0; i &amp;lt; n; i++) sa[i] = i, pos[i] = str[i];
  for(d = 1; ; d &amp;lt;&amp;lt;= 1)
    {
      sort(sa.begin(), sa.end(), cmp);
      for(auto &amp;amp;i: tmp) i = 0;
      for(int i = 0; i &amp;lt; n - 1; i++)
        {
          tmp[i + 1] = tmp[i];
          if(cmp(sa[i], sa[i + 1]) == true) tmp[i + 1]++;
        }
      for(int i = 0; i &amp;lt; n; i++) pos[sa[i]] = tmp[i];

      //모든 접미사가 서로 다른 그룹에 존재
      if(tmp[n - 1] == n - 1) break;
    }

  //struct LCP array
  for(int i = 0, k = 0; i &amp;lt; n; i++, k = max(k - 1, 0))
    {
      //마지막 접미사, 비교할 접미사 존재하지 않음
      if(pos[i] == n - 1) continue;
      //다음 그룹의 접미사
      for(int j = sa[pos[i] + 1]; str[i + k] == str[j + k]; k++);
      lcp[pos[i]] = k;
    }

  return {sa, lcp};
}

vector&amp;lt;int&amp;gt; failureFunction(string substr)
{
  int sublen = substr.length();
  vector&amp;lt;int&amp;gt; pi(sublen);
  int prefix = 0;
  for(int suffix = 1; suffix &amp;lt; sublen; suffix++)
    {
      while(prefix &amp;gt; 0 &amp;amp;&amp;amp; substr[prefix] != substr[suffix]) prefix = pi[prefix - 1];
      if(substr[prefix] == substr[suffix]) pi[suffix] = ++prefix;
    }
  return pi;
}

/* len -&amp;gt; i */
map&amp;lt;int, int&amp;gt; idx;

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0);

  string str;
  cin &amp;gt;&amp;gt; str;

  int n = str.length();
  auto arrays = sa_lcp(str);
  auto sa = arrays.first, lcp = arrays.second;
  lcp.push_back(0);

  auto fail = failureFunction(str);
  for(int i = 0; i &amp;lt; n; i++) idx[n - sa[i]] = i;
  
  vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; res;
  int len = n;
  while(len)
    {
      int i = idx[len];
      int cnt = 0;
      while(lcp[i + cnt] &amp;gt;= len) cnt++;
      res.push_back({len, cnt + 1});
      len = fail[len - 1];
    }
  
  sort(res.begin(), res.end());

  cout &amp;lt;&amp;lt; res.size() &amp;lt;&amp;lt; '\n';
  for(auto ans: res)
    {
      cout &amp;lt;&amp;lt; ans.first &amp;lt;&amp;lt; ' ' &amp;lt;&amp;lt; ans.second &amp;lt;&amp;lt; '\n';
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3. 개억지로 푼 거 아님?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일단 풀기는 했지만, 상당히 불-편합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;애초에 N 제한부터 O(N&amp;sup2;) 풀이를 의도한 것은 아님을 알 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;물론 뭐 세그트리라던가 세그트리라던가 세그트리같은 걸 끼워팔아서 O(NlogN)으로 줄일 수는 있겠지만,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 문제는 약간의 관점 전환으로 훨씬 간단히 해결할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문제에서 확인할 문자열은 접두사이사 접미사인 부분 문자열들입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 접두사라는 사실에 조금 주목을 해 보면, 길이 L의 부분 문자열에 대해서,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;전체 문자열과 각각의 접미사에 대해 LCP가 L 이상이면 해당 접미사의 맨 처음 인덱스에서 매칭이 가능함을 알 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;미리 길이들을 다 구해놨다고 하면, 필요한 것은 '각각의 접미사에 대한 전체 문자열과의 LCP 길이'입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이는 Z 알고리즘으로 선형 시간에 구할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Z array를 활용하면, 사실 실패함수로 길이를 구하지 않아도 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문제의 조건에서 접두사이자 접미사인 문자열의 길이를 요구하고 있고, 각각의 접미사에 대해&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;접두사의 길이와 접미사의 길이가 같다면, 즉 i + z[i] == n이면 구하는 길이는 z[i]가 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LCP의 길이가 L 이상인 접미사의 개수, 이건 그냥 누적합으로 간단히 구할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래는 2차 코드입니다. 실행 시간은 16ms로 확 줄었습니다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1725943428988&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;string&amp;gt;

using namespace std;

/* Z array: 각각의 접미사에 대한 공통 접두사 길이 */
vector&amp;lt;int&amp;gt; zFunction(string &amp;amp;str)
{
  int n = str.length();
  vector&amp;lt;int&amp;gt; z(n);

  z[0] = n;
  int l = 0, r = 0;
  for(int i = 1; i &amp;lt; n; i++)
    {
      if(i &amp;lt; r) z[i] = min(z[i - l], r - i);
      while(i + z[i] &amp;lt; n &amp;amp;&amp;amp; str[z[i]] == str[i + z[i]]) z[i]++;
      if(i + z[i] &amp;gt; r) l = i, r = i + z[i];
    }
  return z;
}

int cnt[100001];

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0);

  string str;
  cin &amp;gt;&amp;gt; str;

  int n = str.length();
  auto z = zFunction(str);

  for(int i = 0; i &amp;lt; n; i++) cnt[z[i]]++;
  for(int i = n - 1; i &amp;gt;= 0; i--) cnt[i] += cnt[i + 1];

  vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; res;
  for(int i = n - 1; i &amp;gt;= 0; i--)
    {
      if(i + z[i] == n) res.push_back({n - i, cnt[z[i]]});
    }

  cout &amp;lt;&amp;lt; res.size() &amp;lt;&amp;lt; '\n';
  for(auto pi: res) cout &amp;lt;&amp;lt; pi.first &amp;lt;&amp;lt; ' ' &amp;lt;&amp;lt; pi.second &amp;lt;&amp;lt; '\n';
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Problem Solving</category>
      <author>thisisuserr</author>
      <guid isPermaLink="true">https://thisisuser.tistory.com/81</guid>
      <comments>https://thisisuser.tistory.com/81#entry81comment</comments>
      <pubDate>Tue, 10 Sep 2024 13:43:54 +0900</pubDate>
    </item>
    <item>
      <title>BOJ 19651. 수열과 쿼리 39</title>
      <link>https://thisisuser.tistory.com/79</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/19651&quot;&gt;19651번: 수열과 쿼리 39 (acmicpc.net)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;solved.ac:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #00b4fc;&quot;&gt;Diamond V&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(2024.09.06)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나이브하게 처리하는 것은 상당히 노답이므로, 문제를 변형시킬 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등차수열에서 주로 사용되는 성질로 A[i-1] + A[i+1] = 2A[i]를 이용합시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 배열을 생각해서 B[i] = A[i-1] + A[i+1] - 2A[i]이라 하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 수 A[i-1], A[i], A[i+1]가 등차수열을 이룰 시 B[i] = 0이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 쿼리의 답은 (연속해서 0인 B[i]의 개수) + 2가 됩니다. 이는 금광세그 트릭을 이용하면 구할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업데이트의 경우 세 수가 모두 포함되거나 포함되지 않을 경우 변화량은 없으므로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리 [s, e]에 대해 B[s - 1], B[s], B[e], B[e + 1]만 업데이트됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 풀이는 코드를 참고해 주세요.&lt;/p&gt;
&lt;pre id=&quot;code_1725451554336&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;
typedef long long int LL;

/*
B[i] = A[i-1] + A[i+1] - 2A[i]로 정의하면, (연속한 0의 개수 + 2)를 구하는 문제로 바뀐다.
구간 [s, e]에 (x + nd)를 더한다고 해 보자.

B[s - 1] = A[s - 2] + A[s] - 2A[s - 1]에서, 변화량은 x이다.
B[s] = A[s - 1] + A[s + 1] - 2A[s]에서, 변화량은 d - x이다.

B[e] = A[e - 1] + A[e + 1] - 2A[e]에서, 변화량은 (s - e - 1)d - x이다.
B[e + 1] = A[e] + A[e + 2] - 2A[e + 1]에서, 변화량은 (e - s)d + x이다.
*/

/* 연속한 0의 개수 관리: 금광 세그 */
struct seg
{
bool all;
int lmax, rmax, mx, sz;
};

LL arr[400001];
seg tree[400001];

seg merge(seg &amp;amp;l, seg &amp;amp;r)
{
  seg res;
  res.all = l.all &amp;amp;&amp;amp; r.all;
  res.lmax = (l.all ? l.sz + r.lmax : l.lmax);
  res.rmax = (r.all ? l.rmax + r.sz : r.rmax);
  res.mx = max(max(l.mx, r.mx), l.rmax + r.lmax);
  res.sz = l.sz + r.sz;
  return res;
}

void update(int node, int s, int e, int idx, int val)
{
  if(idx &amp;lt; s || e &amp;lt; idx) return;
  if(s == e) // update
  {
    arr[node] += val;
    tree[node].sz = 1;
    
    if(arr[node] == 0)
    {
      tree[node].lmax = 1; tree[node].rmax = 1; 
      tree[node].mx = 1;   tree[node].all = true;
    }
    else
    {
      tree[node].lmax = 0; tree[node].rmax = 0;
      tree[node].mx = 0;   tree[node].all = false;
    }
    return;
  }

  int m = (s + e) / 2;
  update(node * 2, s, m, idx, val);
  update(node * 2 + 1, m + 1, e, idx, val);

  tree[node] = merge(tree[node * 2], tree[node * 2 + 1]);
}

seg query(int node, int s, int e, int l, int r)
{
  if(r &amp;lt; s || e &amp;lt; l) return {true, 0, 0, 0, 0};
  if(l &amp;lt;= s &amp;amp;&amp;amp; e &amp;lt;= r) return tree[node];

  int m = (s + e) / 2;
  seg left = query(node * 2, s, m, l, r);
  seg right = query(node * 2 + 1, m + 1, e, l, r);
  return merge(left, right);
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0);

  int n;
  cin &amp;gt;&amp;gt; n;

  vector&amp;lt;int&amp;gt; init(n + 1);
  for(int i = 1; i &amp;lt;= n; i++) cin &amp;gt;&amp;gt; init[i];
  for(int i = 2; i &amp;lt; n; i++) update(1, 1, n, i, init[i - 1] + init[i + 1] - 2 * init[i]);

  int Q;
  cin &amp;gt;&amp;gt; Q;
  while(Q--)
    {
      int op;
      cin &amp;gt;&amp;gt; op;

      if(op == 1)
      {
        int s, e, x, d;
        cin &amp;gt;&amp;gt; s &amp;gt;&amp;gt; e &amp;gt;&amp;gt; x &amp;gt;&amp;gt; d;

        update(1, 1, n, s - 1, x);
        update(1, 1, n, s, d - x);
        update(1, 1, n, e, (s - e - 1) * d - x);
        update(1, 1, n, e + 1, (e - s) * d + x);
      }
      else
      {
        int l, r;
        cin &amp;gt;&amp;gt; l &amp;gt;&amp;gt; r;
        cout &amp;lt;&amp;lt; query(1, 1, n, l + 1, r - 1).mx + 2 &amp;lt;&amp;lt; '\n';
      }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Problem Solving</category>
      <author>thisisuserr</author>
      <guid isPermaLink="true">https://thisisuser.tistory.com/79</guid>
      <comments>https://thisisuser.tistory.com/79#entry79comment</comments>
      <pubDate>Wed, 4 Sep 2024 21:06:11 +0900</pubDate>
    </item>
    <item>
      <title>BOJ 12963. 달리기</title>
      <link>https://thisisuser.tistory.com/78</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/12963&quot;&gt;12963번: 달리기 (acmicpc.net)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;solved.ac: &lt;span style=&quot;color: #27e2a4;&quot;&gt;Platinum 3&lt;/span&gt; (2024.09.06)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플로우 안 쓰는 플로우 문제입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 문제를 딱 보자마자 최대 유량 문제구나를 파악할 수 있는데 범위가 노답입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 유량을 구하는 문제이므로 최소 컷을 구해야 함을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 용량이 특수하게 주어지므로, 다음을 이용합시다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;1. 최대 유량을 흘렸을 때, 유량이 존재하는 간선을 모두 제거하면 보낼 수 있는 유량은 0이다.&lt;br /&gt;2. i번 간선에 유량이 흐른다면 이 유량으로 0번부터 i-1번 간선에 모두 유량을 보낼 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0번 간선에서 N-1번 간선까지의 어떠한 경로가 존재한다면, 유량은 당연히 가장 작은 간선의 용량이 될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 2번에 의해, 여러 개의 경로가 존재할 때 일부 간선이 겹쳐도 전혀 문제될 게 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 모든 간선을 연결해도 두 정점이 연결되지 않는다면, 당연히 유량은 0입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 어떠한 간선이 있고, 해당 간선을 연결하면 0번과 N-1번 정점이 연결된다고 가정해 보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mO9yo/btsJoOBVvpC/WHtxH8LKZLgdlJlfkzRkgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mO9yo/btsJoOBVvpC/WHtxH8LKZLgdlJlfkzRkgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mO9yo/btsJoOBVvpC/WHtxH8LKZLgdlJlfkzRkgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmO9yo%2FbtsJoOBVvpC%2FWHtxH8LKZLgdlJlfkzRkgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;149&quot; height=&quot;276&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;836&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프 상에서 현재 최소 용량 간선은 연결되어 있지 않다고 가정해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 0번 정점과 N-1번 정점을 잇는 또 다른 경로를 찾았다고 하면, 다음과 같이 일반화가 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAsRRx/btsJn0CV53r/KL5XjsOuVm96TrlDICpyTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAsRRx/btsJn0CV53r/KL5XjsOuVm96TrlDICpyTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAsRRx/btsJn0CV53r/KL5XjsOuVm96TrlDICpyTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAsRRx%2FbtsJn0CV53r%2FKL5XjsOuVm96TrlDICpyTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;180&quot; height=&quot;333&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;836&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 색칠된 간선의 용량이 가장 작고, a에 충분히 보내도 될 만큼의 유량이 흐른다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 유량은 이전에 미리 빼두었던 간선의 용량과 현재 칠해진 간선의 유량의 합이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충분히 보내도 될 만큼의 유량이 흐른다, 이는 위의 2번 정리를 이용하면 될 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 그림에서 빨간색 간선의 용량이 파란색 간선의 용량보다 크다면, 빨간색 간선을 통해 흘린 유량은 두 파란 간선에 모두 흘리고도 남습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1Co05/btsJp9dOpEr/uAOYTzBUkNiBq2cfzlbYrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1Co05/btsJp9dOpEr/uAOYTzBUkNiBq2cfzlbYrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1Co05/btsJp9dOpEr/uAOYTzBUkNiBq2cfzlbYrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1Co05%2FbtsJp9dOpEr%2FuAOYTzBUkNiBq2cfzlbYrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;180&quot; height=&quot;333&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;836&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 어떠한 간선으로 연결을 시도했을 때 두 정점이 연결이 된다. 이때 용량은 가장 작아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠깐만 생각해보면 그냥 간선 용량을 큰 것부터 연결해 보면 됨을 알 수 있습니다. 이렇게 구성하면 충분히 보내도 될 만큼의 유량이 흐르는 것도 자연스럽게 만들어지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 간선을 연결했을 때 0번 정점과 N-1번 정점이 연결되는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 분리 집합 자료구조를 이용하면 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인하려는 간선이 잇는 두 정점을 p, q라 하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(0과 p, q와 n-1이 연결되어 있다) or (0과 q, p와 n-1이 연결되어 있다) 를 만족시키면 연결되는 간선임을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현은 꽤나 간단합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1725334402982&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;
typedef long long int LL;
const LL mod = 1e9+7;

/* Disjoint Set */
int parent[2001];

void init()
{
  for(int i = 0; i &amp;lt; 2001; i++) parent[i] = i;
}

int find(int x)
{
  if(parent[x] == x) return x;
  else return parent[x] = find(parent[x]);
}

void merge(int p, int q)
{
  p = find(p);
  q = find(q);
  if(p != q) parent[q] = p;
}

struct edge
{
LL cost;
int p, q;
};

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0);

  int n, m;
  cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;

  vector&amp;lt;edge&amp;gt; edges;
  init();

  LL cur = 1;
  for(int i = 0; i &amp;lt; m; i++)
    {
      int p, q;
      cin &amp;gt;&amp;gt; p &amp;gt;&amp;gt; q;
      edges.push_back({cur, p, q});
      cur = (cur * 3) % mod;
    }

  LL res = 0;
  for(int i = m - 1; i &amp;gt;= 0; i--)
    {
      LL cost = edges[i].cost;
      LL p = edges[i].p, q = edges[i].q;

      if((find(p) == find(0) &amp;amp;&amp;amp; find(q) == find(n - 1)) || (find(p) == find(n - 1) &amp;amp;&amp;amp; find(q) == find(0)))
      {
        res = (res + cost) % mod;
      }
      else merge(p, q);
    }
  cout &amp;lt;&amp;lt; res;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Problem Solving</category>
      <author>thisisuserr</author>
      <guid isPermaLink="true">https://thisisuser.tistory.com/78</guid>
      <comments>https://thisisuser.tistory.com/78#entry78comment</comments>
      <pubDate>Tue, 3 Sep 2024 12:33:27 +0900</pubDate>
    </item>
    <item>
      <title>BOJ 17501. 수식 트리</title>
      <link>https://thisisuser.tistory.com/77</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;솔직히 왜 골드2인지 모르겠는 문제. 간단한 트리 DP?를 통해 해결했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17501&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문제&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;solved.ac:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ec9a00;&quot;&gt;Gold II&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(2024.09.06)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어차피 식을 모두 합치면, (a1 + a2 + ... ) - (b1 + b2 + ... ) 꼴이 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 최대한 작은 수들을 빼면 된다. 이건 정렬 + 그리디로 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 서브트리와 두 서브트리에서 빼야 하는 노드의 개수를 각각 A, B개라 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 현재 노드가 +라면, 이 서브트리에서 빼야 하는 노드의 개수는 자명히 A + B개가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 현재 노드가 -라면, 원래 빼야 했던 노드는 다 더해지고, 원래 더해야 했던 노드들이 빼지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 우측 서브트리의 트리 크기를 sz라 하면, 빼야 하는 노드의 개수는 A + (sz - B)개가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 총 빼야 하는 노드의 수 s를 구할 수 있고, 오름차순 정렬 후 앞에서부터 s개는 빼고, 나머지는 더하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1724975436666&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int n;
int dp[200001], sz[200001];
string tree[200001];

int child[200001][2];

void DP(int cur)
{
  if(cur &amp;lt;= n)
  {
    sz[cur] = 1;
    dp[cur] = 0;
    return;
  }

  int l = child[cur][0], r = child[cur][1];
  DP(l); DP(r);

  sz[cur] = sz[l] + sz[r];
  if(tree[cur] == &quot;+&quot;) dp[cur] = dp[l] + dp[r];
  else dp[cur] = dp[l] + (sz[r] - dp[r]);
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0);

  cin &amp;gt;&amp;gt; n;

  vector&amp;lt;int&amp;gt; arr(n);
  for(int i = 0; i &amp;lt; n; i++) cin &amp;gt;&amp;gt; arr[i];
  for(int i = n + 1; i &amp;lt; 2 * n; i++) cin &amp;gt;&amp;gt; tree[i] &amp;gt;&amp;gt; child[i][0] &amp;gt;&amp;gt; child[i][1];

  DP(2 * n - 1);
  sort(arr.begin(), arr.end());

  int res = 0, idx = dp[2 * n - 1];
  for(int i = 0; i &amp;lt; n; i++)
    {
      if(i &amp;lt; idx) res -= arr[i];
      else res += arr[i];
    }
  cout &amp;lt;&amp;lt; res;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Problem Solving</category>
      <author>thisisuserr</author>
      <guid isPermaLink="true">https://thisisuser.tistory.com/77</guid>
      <comments>https://thisisuser.tistory.com/77#entry77comment</comments>
      <pubDate>Fri, 30 Aug 2024 08:50:39 +0900</pubDate>
    </item>
    <item>
      <title>BOJ 11378. 열혈강호 4</title>
      <link>https://thisisuser.tistory.com/76</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11378&quot;&gt;11378번: 열혈강호 4 (acmicpc.net)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;solved.ac:&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #27e2a4;&quot;&gt;Platinum III&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(2024.09.10)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서 시키는 대로 모델링하고 맞을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델링 방법이 조금씩 다른 것 같긴 하다. 본인은 다음과 같이 모델링했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;918&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpEtS1/btsJfoRJZOv/dKcLondKqlMPH90HRKVioK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpEtS1/btsJfoRJZOv/dKcLondKqlMPH90HRKVioK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpEtS1/btsJfoRJZOv/dKcLondKqlMPH90HRKVioK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpEtS1%2FbtsJfoRJZOv%2FdKcLondKqlMPH90HRKVioK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;591&quot; height=&quot;363&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;918&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1724727997418&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;cstring&amp;gt;

using namespace std;
const int INF = 1e9;

vector&amp;lt;int&amp;gt; node[2005];
int capacity[2005][2005];
int flow[2005][2005];

int networkFlow(int source, int sink)
{
  int res = 0;
  int parent[2005];

  while(1)
    {
      memset(parent, -1, sizeof(parent));
      queue&amp;lt;int&amp;gt; q;
      parent[source] = source;
      q.push(source);

      while(!q.empty() &amp;amp;&amp;amp; parent[sink] == -1)
        {
          int cur = q.front();
          q.pop();

          for(auto nxt: node[cur])
            {
              if(capacity[cur][nxt] &amp;gt; flow[cur][nxt] &amp;amp;&amp;amp; parent[nxt] == -1)
              {
                parent[nxt] = cur;
                q.push(nxt);
              }
            }
        }
      if(parent[sink] == -1) break;

      int amount = INF;
      for(int cur = sink; cur != source; cur = parent[cur])
        {
          int prv = parent[cur];
          amount = min(amount, capacity[prv][cur] - flow[prv][cur]);
        }
      for(int cur = sink; cur != source; cur = parent[cur])
        {
          int prv = parent[cur];
          flow[prv][cur] += amount;
          flow[cur][prv] -= amount;
        }
      res += amount;
    }
  return res;
}

void addNode(int from, int to, int cap, bool is_inv = false)
{
  node[from].push_back(to);
  node[to].push_back(from);
  capacity[from][to] += cap;
  if(is_inv) capacity[to][from] += cap;
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0);

  int source = 2001, sink = 2002, punish = 2003;
  int n, m, k;
  cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m &amp;gt;&amp;gt; k;

  addNode(source, punish, k);
  for(int i = 1; i &amp;lt;= n; i++)
    {
      addNode(source, i, 1);
      addNode(punish, i, k);
    }
  for(int i = 1; i &amp;lt;= m; i++) addNode(i + 1000, sink, 1);
  for(int i = 1; i &amp;lt;= n; i++)
    {
      int sz;
      cin &amp;gt;&amp;gt; sz;
      while(sz--)
        {
          int nxt;
          cin &amp;gt;&amp;gt; nxt;
          addNode(i, nxt + 1000, 1);
        }
    }
  cout &amp;lt;&amp;lt; networkFlow(source, sink);
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Problem Solving</category>
      <author>thisisuserr</author>
      <guid isPermaLink="true">https://thisisuser.tistory.com/76</guid>
      <comments>https://thisisuser.tistory.com/76#entry76comment</comments>
      <pubDate>Tue, 27 Aug 2024 12:06:45 +0900</pubDate>
    </item>
    <item>
      <title>BOJ 1867. 돌멩이 제거</title>
      <link>https://thisisuser.tistory.com/75</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1867&quot;&gt;1867번: 돌멩이 제거 (acmicpc.net)&lt;/a&gt;&lt;b&gt;&lt;br /&gt;solved.ac:&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #27e2a4;&quot;&gt;Platinum III&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(2024.09.10)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a행 b열에 놓인 돌멩이를 제거하기 위해서는 a행을 쓸거나 b열을 쓸어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프로 환원시키면, 1~n행과 1~m행 정점을 만들어 놓고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간선을 돌로 정의해서, 해당 간선이 잇는 두 정점 중 하나가 색칠되면 된다라고 할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;654&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L4fjW/btsJgJG8j06/hKjKhXKn9GnYHzoo26vYT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L4fjW/btsJgJG8j06/hKjKhXKn9GnYHzoo26vYT0/img.png&quot; data-alt=&quot;예제&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L4fjW/btsJgJG8j06/hKjKhXKn9GnYHzoo26vYT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL4fjW%2FbtsJgJG8j06%2FhKjKhXKn9GnYHzoo26vYT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;256&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;654&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예제&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 1행을 쓸고, 2열을 쓸면 된다. 그래프로 나타내면, 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;654&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtu4oE/btsJhia96OY/XC4TPYmmRgzdfVgj3GWQjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtu4oE/btsJhia96OY/XC4TPYmmRgzdfVgj3GWQjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtu4oE/btsJhia96OY/XC4TPYmmRgzdfVgj3GWQjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdtu4oE%2FbtsJhia96OY%2FXC4TPYmmRgzdfVgj3GWQjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;256&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;654&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 문제를 재정의하면 이분 그래프에서 최소한의 정점으로 모든 간선을 칠하는 방법을 찾는 문제가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 최소 정점 커버 문제 (Minimum Vertex Cover)로 알려져 있고, 이분 그래프의 경우 다항 시간 해법이 존재한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말하면, 이분 그래프에서 최소 정점 커버의 해답은 이분 매칭의 최대 수와 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 쾨닉의 정리(Konig's Theorem)로 잘 알려져 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;증명은 꽤나 어려워 보인다. 나중에 다시 공부해 봐야겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1724721445586&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

/* 0 - based */

bool dfs(int cur, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; &amp;amp;node, vector&amp;lt;int&amp;gt; &amp;amp;fromB, vector&amp;lt;bool&amp;gt; &amp;amp;vis)
{
  for(auto nxt: node[cur])
    {
      if(vis[nxt]) continue;
      vis[nxt] = true;

      if(fromB[nxt] == -1 || dfs(fromB[nxt], node, fromB, vis))
      {
        fromB[nxt] = cur;
        return true;
      }
    }
  return false;
}

vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; BipartiteMatch(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; &amp;amp;node)
{
  int n = node.size();
  vector&amp;lt;int&amp;gt; fromB(n, -1);
  vector&amp;lt;bool&amp;gt; vis(n);

  for(int i = 0; i &amp;lt; n; i++)
    {
      vis.assign(n, false);
      dfs(i, node, fromB, vis);
    }

  vector&amp;lt;pair&amp;lt;int, int&amp;gt;&amp;gt; res;
  for(int i = 0; i &amp;lt; n; i++) if(fromB[i] != -1) res.push_back({fromB[i], i});
  sort(res.begin(), res.end());
  return res;
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0);

  int n, k;
  cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; k;

  vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; node(2 * n);
  for(int i = 0; i &amp;lt; k; i++)
    {
      int x, y;
      cin &amp;gt;&amp;gt; x &amp;gt;&amp;gt; y;
      x--; y--;

      node[x].push_back(y + n);
    }
  cout &amp;lt;&amp;lt; BipartiteMatch(node).size();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Problem Solving</category>
      <author>thisisuserr</author>
      <guid isPermaLink="true">https://thisisuser.tistory.com/75</guid>
      <comments>https://thisisuser.tistory.com/75#entry75comment</comments>
      <pubDate>Tue, 27 Aug 2024 10:17:30 +0900</pubDate>
    </item>
    <item>
      <title>BOJ 11012. Egg</title>
      <link>https://thisisuser.tistory.com/74</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11012&quot;&gt;11012번: Egg (acmicpc.net)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;solved.ac:&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #27e2a4;&quot;&gt;Platinum II&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(2024.09.10)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 직사각형별로 경계 또는 내부에 있는 점의 수를 세어 더하는 문제다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;345&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UhLSH/btsJgIVD7ZV/juV0deUd84IPnzxwtctaDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UhLSH/btsJgIVD7ZV/juV0deUd84IPnzxwtctaDk/img.png&quot; data-alt=&quot;예제 2번 테스트케이스.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UhLSH/btsJgIVD7ZV/juV0deUd84IPnzxwtctaDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUhLSH%2FbtsJgIVD7ZV%2FjuV0deUd84IPnzxwtctaDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;317&quot; height=&quot;238&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;345&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예제 2번 테스트케이스.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 직사각형을 하나씩 구해 보는 건 당연히 불가능할 것임을 짐작할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 생각을 바꿔서, 각 점별로 세지는 횟수를 구해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점별로 구하기 위해서는, 해당 점에 몇 개의 직사각형이 겹쳐 있는 지 파악해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽부터 훑으면서 직사각형을 관리하는 것은 스위핑 기법이 잘 알려져 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 직사각형의 시작과 끝을 구분해 보자. 푸른 색이 새로운 직사각형 시작, 빨간 색이 직사각형 끝이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;704&quot; data-origin-height=&quot;499&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b35hSY/btsJgupS0Ic/3fPgmj39hhQ59WPWiS9OU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b35hSY/btsJgupS0Ic/3fPgmj39hhQ59WPWiS9OU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b35hSY/btsJgupS0Ic/3fPgmj39hhQ59WPWiS9OU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb35hSY%2FbtsJgupS0Ic%2F3fPgmj39hhQ59WPWiS9OU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;336&quot; height=&quot;238&quot; data-origin-width=&quot;704&quot; data-origin-height=&quot;499&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 왼쪽부터 스위핑을 하면서, 구간 업데이트를 통해 해당 위치에서의 직사각형 개수를 구하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 lazyprop 세그트리를 이용하면 간단히 구할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 두 번째 위치에서 쿼리 순서를 결정해야 하는 부분이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 경계에서 직사각형이 끝난다 해도, 해당 위치에 점이 있다면 그 끝나는 직사각형에 걸치게 되므로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직사각형 종료 쿼리는 점 계산 쿼리보다 나중에 진행해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 동일한 x좌표의 쿼리에 우선순위를 매기면 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;새로운 직사각형 생성&lt;/li&gt;
&lt;li&gt;점의 위치에 따른 직사각형 개수 계산&lt;/li&gt;
&lt;li&gt;직사각형 종료&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 구현만 하면 끝난다. 코드는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1724680387995&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;
typedef long long int LL;

LL tree[400001], lazy[400001];

void propagate(int node, int s, int e)
{
  if(!lazy[node]) return;

  tree[node] += (e - s + 1) * lazy[node];
  if(s != e)
  {
    lazy[node * 2] += lazy[node];
    lazy[node * 2 + 1] += lazy[node];
  }
  lazy[node] = 0;
}

LL update(int node, int s, int e, int l, int r, int val)
{
  propagate(node, s, e);

  if(r &amp;lt; s || e &amp;lt; l) return tree[node];
  if(l &amp;lt;= s &amp;amp;&amp;amp; e &amp;lt;= r)
  {
    tree[node] += val * (e - s + 1);
    if(s != e)
    {
      lazy[node * 2] += val;
      lazy[node * 2 + 1] += val;
    }
    return tree[node];
  }

  int m = (s + e) / 2;
  LL left = update(node * 2, s, m, l, r, val);
  LL right = update(node * 2 + 1, m + 1, e, l, r, val);
  return tree[node] = left + right;
}

LL query(int node, int s, int e, int idx)
{
  propagate(node, s, e);
  if(idx &amp;lt; s || e &amp;lt; idx) return 0;
  if(s == e) return tree[node];

  int m = (s + e) / 2;
  LL left = query(node * 2, s, m, idx);
  LL right = query(node * 2 + 1, m + 1, e, idx);
  return left + right;
}

void init()
{
  memset(tree, 0, sizeof(tree));
  memset(lazy, 0, sizeof(lazy));
}

struct Query
{
int op;
int x, y1, y2;

bool operator&amp;lt; (const Query &amp;amp;l)
{
  if(x != l.x) return x &amp;lt; l.x;
  return op &amp;gt; l.op;
}
};

void solve()
{
  init();
  int n, m;
  cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;

  vector&amp;lt;Query&amp;gt; arr;
  while(n--)
    {
      int x, y;
      cin &amp;gt;&amp;gt; x &amp;gt;&amp;gt; y;
      arr.push_back({0, x, y, y});
    }
  while(m--)
    {
      int x1, x2, y1, y2;
      cin &amp;gt;&amp;gt; x1 &amp;gt;&amp;gt; x2 &amp;gt;&amp;gt; y1 &amp;gt;&amp;gt; y2;

      arr.push_back({1, x1, y1, y2});
      arr.push_back({-1, x2, y1, y2});
    }
  sort(arr.begin(), arr.end());

  LL res = 0;
  for(auto i: arr)
    {
      int op = i.op;
      int y1 = i.y1, y2 = i.y2;

      if(op == 0) res += query(1, 0, 100000, y1);
      else update(1, 0, 100000, y1, y2, op);
    }
  cout &amp;lt;&amp;lt; res &amp;lt;&amp;lt; '\n';
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0);

  int T;
  cin &amp;gt;&amp;gt; T;
  while(T--) solve();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여담&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;412&quot; data-origin-height=&quot;55&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uWr07/btsJgJNM1Mr/NJLDAOiaxRt8K5XQhyImKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uWr07/btsJgJNM1Mr/NJLDAOiaxRt8K5XQhyImKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uWr07/btsJgJNM1Mr/NJLDAOiaxRt8K5XQhyImKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuWr07%2FbtsJgJNM1Mr%2FNJLDAOiaxRt8K5XQhyImKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;352&quot; height=&quot;55&quot; data-origin-width=&quot;412&quot; data-origin-height=&quot;55&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이거 왜돼요&lt;/p&gt;</description>
      <category>Problem Solving</category>
      <author>thisisuserr</author>
      <guid isPermaLink="true">https://thisisuser.tistory.com/74</guid>
      <comments>https://thisisuser.tistory.com/74#entry74comment</comments>
      <pubDate>Mon, 26 Aug 2024 22:54:38 +0900</pubDate>
    </item>
    <item>
      <title>BOJ 7626. 직사각형</title>
      <link>https://thisisuser.tistory.com/73</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/7626&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/7626&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;solved.ac:&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #27e2a4;&quot;&gt;Platinum I&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(2024.09.10)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직사각형이 여러 개가 주어질 때 합집합의 넓이를 구하라는 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참 별 게 다 웰노운이다 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 x축 기준으로 쭉 정렬한 다음, 세로 선분들을 관리해주고 대충 스위핑질해주는 풀이이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1647&quot; data-origin-height=&quot;736&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ls9MC/btsJf8HqxYl/3VeO9E7UXIGBTBZFwKsKX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ls9MC/btsJf8HqxYl/3VeO9E7UXIGBTBZFwKsKX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ls9MC/btsJf8HqxYl/3VeO9E7UXIGBTBZFwKsKX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fls9MC%2FbtsJf8HqxYl%2F3VeO9E7UXIGBTBZFwKsKX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;567&quot; height=&quot;253&quot; data-origin-width=&quot;1647&quot; data-origin-height=&quot;736&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세그먼트 트리 사용이 조금 특이하긴 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 가지 변수를 관리해주는데, 구간별 누적 길이랑 전체를 덮은 횟수를 기록해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체를 덮은 횟수가 1 이상이라면 해당 구간의 값은 전체 길이와 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇지 않다면 두 자식 노드로 쪼개고 그 합을 구하여 해결한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;범위가 크니 좌표 압축을 해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딱히 쓸 게 없네요&lt;/p&gt;
&lt;pre id=&quot;code_1724674977393&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;map&amp;gt;

using namespace std;
typedef long long int LL;

struct line
{
LL x;
LL y1, y2;
int val;

bool operator&amp;lt; (const line&amp;amp; l){return x &amp;lt; l.x;}
};

vector&amp;lt;line&amp;gt; lines;
vector&amp;lt;LL&amp;gt; comp;

LL tree[2000001], cnt[2000001];

void update(int node, int s, int e, int l, int r, int val)
{
  if(r &amp;lt; s || e &amp;lt; l) return;
  if(l &amp;lt;= s &amp;amp;&amp;amp; e &amp;lt;= r) cnt[node] += val;
  else
  {
    int m = (s + e) / 2;
    update(node * 2, s, m, l, r, val);
    update(node * 2 + 1, m + 1, e, l, r, val);
  }

  if(cnt[node]) tree[node] = comp[e] - comp[s - 1];
  else
  {
    if(s == e) tree[node] = 0;
    else tree[node] = tree[node * 2] + tree[node * 2 + 1];
  }
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0);

  int n;
  cin &amp;gt;&amp;gt; n;
  for(int i = 0; i &amp;lt; n; i++)
    {
      LL x1, x2, y1, y2;
      cin &amp;gt;&amp;gt; x1 &amp;gt;&amp;gt; x2 &amp;gt;&amp;gt; y1 &amp;gt;&amp;gt; y2;

      lines.push_back({x1, y1, y2, 1});
      lines.push_back({x2, y1, y2, -1});
      comp.push_back(y1);
      comp.push_back(y2);
    }

  sort(comp.begin(), comp.end());
  comp.erase(unique(comp.begin(), comp.end()), comp.end());

  sort(lines.begin(), lines.end());

  LL res = 0;
  line prv = lines[0];

  for(int i = 0; i &amp;lt; 2 * n; i++)
    {
      line cur = lines[i];
      LL dx = cur.x - prv.x;
      res += tree[1] * dx;

      LL s = lower_bound(comp.begin(), comp.end(), cur.y1) - comp.begin();
      LL e = lower_bound(comp.begin(), comp.end(), cur.y2) - comp.begin();
      update(1, 1, comp.size() + 1, s + 1, e, cur.val);
      prv = cur;
    }
  cout &amp;lt;&amp;lt; res;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Problem Solving</category>
      <author>thisisuserr</author>
      <guid isPermaLink="true">https://thisisuser.tistory.com/73</guid>
      <comments>https://thisisuser.tistory.com/73#entry73comment</comments>
      <pubDate>Mon, 26 Aug 2024 21:23:08 +0900</pubDate>
    </item>
    <item>
      <title>Aho-Corasick 알고리즘</title>
      <link>https://thisisuser.tistory.com/72</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 말해 트라이 위에서 KMP를 수행하는 알고리즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짤막하게 트라이와 KMP를 짚어보고 넘어가겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 트라이(Trie)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;N개의 길이 M 문자열 리스트가 있을 때, 어떤 문자열이 해당 리스트에 포함되어 있는 지 판단하는 알고리즘을 짠다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Naive하게 짜면 $O(NM)$일 것이고, 여기서 더 줄일 수 있는 방법은 딱히 없어 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(정렬을 하면 $O(MlogN)$으로 줄어들긴 하겠지만, 그 과정이 애초에 비효율적이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트라이는 트리 자료구조에서 착안하여, 무려 $O(M)$에 찾을 수 있도록 구성해준 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 다음과 같이, 트리를 따라 리프 노드까지 쭉 따라가면서 순서대로 합친 것을 문자열로 생각한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;329&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wDQyc/btsJbq8syMR/YtRUV5K5SM0vlYURP0rRkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wDQyc/btsJbq8syMR/YtRUV5K5SM0vlYURP0rRkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wDQyc/btsJbq8syMR/YtRUV5K5SM0vlYURP0rRkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwDQyc%2FbtsJbq8syMR%2FYtRUV5K5SM0vlYURP0rRkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;473&quot; height=&quot;228&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;329&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 포인터를 활용하여 구성한다. 아래는 트라이 코드의 일부이다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1724230985902&quot; class=&quot;cpp&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct node
{
node* c[26]; //자식노드
int cnt; //자식노드 개수
bool fin; //단어의 끝 성분인가

node()
{
  for(int i=0;i&amp;lt;26;i++) c[i]=NULL;
  cnt=0; fin=false;
}
~node()
{
  for(int i=0;i&amp;lt;26;i++) if(c[i]!=NULL) delete c[i];
}

void insert(string &amp;amp;s, int idx)
{
  if(idx==s.length()) //더 이상 넣을 문자가 없다(현재 노드가 단어의 끝)
  {
    fin=true;
    return;
  }
  if(c[s[idx]-'a']==NULL)
  {
    c[s[idx]-'a']=new node();
    cnt++;
  }
  c[s[idx]-'a']-&amp;gt;insert(s,idx+1);
}
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. KMP 알고리즘&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 문자열이 다른 문자열의 부분 문자열인 지를 $O(N+M)$에 탐색할 수 있도록 하는 알고리즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실패 함수라는 개념이 필요한데, 각각의 접두사 문자열에 대해 접두사와 접미사가 같아지는 최대 길이(전체 빼고)를 기록한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;90&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdd81Z/btsI9OiRHvr/PMcdtt2ScFDxkIh36zze81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdd81Z/btsI9OiRHvr/PMcdtt2ScFDxkIh36zze81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdd81Z/btsI9OiRHvr/PMcdtt2ScFDxkIh36zze81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdd81Z%2FbtsI9OiRHvr%2FPMcdtt2ScFDxkIh36zze81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;671&quot; height=&quot;80&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;90&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 구해 놓으면 매칭을 쭉 돌리다 실패했을 때 많은 작업을 건너뛸 수 있게 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대충 이러한 탐색 과정을 가지고 있다고 해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;410&quot; data-origin-height=&quot;101&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DYjVb/btsJbLq1AVn/ZDeblV0BhgRqf3nbA3bkRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DYjVb/btsJbLq1AVn/ZDeblV0BhgRqf3nbA3bkRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DYjVb/btsJbLq1AVn/ZDeblV0BhgRqf3nbA3bkRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDYjVb%2FbtsJbLq1AVn%2FZDeblV0BhgRqf3nbA3bkRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;410&quot; height=&quot;101&quot; data-origin-width=&quot;410&quot; data-origin-height=&quot;101&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 무식하게 한 칸만 옮기는 게 아니라, 미리 구해놓은 실패함수를 이용해 이렇게 확 건너뛸 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;403&quot; data-origin-height=&quot;98&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Prh9W/btsJbdnXLEa/C77lXDq6BC6i5Y2q1TO0i1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Prh9W/btsJbdnXLEa/C77lXDq6BC6i5Y2q1TO0i1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Prh9W/btsJbdnXLEa/C77lXDq6BC6i5Y2q1TO0i1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPrh9W%2FbtsJbdnXLEa%2FC77lXDq6BC6i5Y2q1TO0i1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;403&quot; height=&quot;98&quot; data-origin-width=&quot;403&quot; data-origin-height=&quot;98&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 본인이 사용하는 KMP 템플릿이다. 아무래도 메인 주제는 아닌 만큼 대충 넘어가겠다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1724231398440&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;

/* KMP Template, returns matching prefix index vector */
vector&amp;lt;int&amp;gt; KMP(string str, string substr)
{
  const int len = str.length();
  const int sublen = substr.length();
  vector&amp;lt;int&amp;gt; res;

  /* Construct Failure Function */
  vector&amp;lt;int&amp;gt; pi(sublen);
  int prefix = 0;
  for(int suffix = 1; suffix &amp;lt; sublen; suffix++)
    {
      while(prefix &amp;gt; 0 &amp;amp;&amp;amp; substr[prefix] != substr[suffix]) prefix = pi[prefix - 1];
      if(substr[prefix] == substr[suffix]) pi[suffix] = ++prefix;
    }

  /* Find */
  int sub_idx = 0;
  for(int str_idx = 0; str_idx &amp;lt; len; str_idx++)
    {
      while(sub_idx &amp;gt; 0 &amp;amp;&amp;amp; str[str_idx] != substr[sub_idx]) sub_idx = pi[sub_idx - 1];
      if(str[str_idx] == substr[sub_idx])
      {
        sub_idx++;
        if(sub_idx == sublen)
        {
          res.push_back(str_idx - (sublen - 1));
          sub_idx = pi[sub_idx - 1];
        }
      }
    }
  return res;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지가, 배경지식이다. BFS도 필요하긴 한데 솔직히 이거 할 정도면 당연히 알 거라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 아호-코라식 (Aho-Corasick)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약하면 이 두개를 짬뽕시킨 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 트라이 위에서 하는 실패함수 구축 과정이 KMP와 거의 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 KMP를 알고 있다는 전제 하에 글을 작성한다. 설명하기 어렵기도 하고.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 실패함수를 구축하려면 현재 탐색 중인 노드의 모든 조상들에 대해 이미 실패함수가 구축되어 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 트라이 탐색을 너비 우선 탐색으로 함으로써 간단히 해결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 한 노드에 대해서, 계속 부모 노드를 타고 올라가 얻은 문자열에 대해 KMP를 수행한다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 트리를 예시로 들어 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;1071&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yMkDE/btsJeRFJcTp/t1HXkkoMwjTRW10MUmhK41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yMkDE/btsJeRFJcTp/t1HXkkoMwjTRW10MUmhK41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yMkDE/btsJeRFJcTp/t1HXkkoMwjTRW10MUmhK41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyMkDE%2FbtsJeRFJcTp%2Ft1HXkkoMwjTRW10MUmhK41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;358&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;1071&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 부모 노드가 루트라면, 그 노드의 실패함수는 루트 노드이다. 실패함수의 정의에 의해, 1글자일 경우 실패함수는 항상 0이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;1071&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baDrNM/btsJd31fj2y/aaQXAwI1GnyENL5H7hfrK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baDrNM/btsJd31fj2y/aaQXAwI1GnyENL5H7hfrK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baDrNM/btsJd31fj2y/aaQXAwI1GnyENL5H7hfrK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaDrNM%2FbtsJd31fj2y%2FaaQXAwI1GnyENL5H7hfrK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;358&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;1071&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A의 자식노드 B에 대하여 현재까지의 문자열은 AB이다. 마찬가지로 실패함수는 루트 노드로 간다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;1071&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSf1Vl/btsJfb42hYb/KgwiNk9asMKeIgQqz3CWYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSf1Vl/btsJfb42hYb/KgwiNk9asMKeIgQqz3CWYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSf1Vl/btsJfb42hYb/KgwiNk9asMKeIgQqz3CWYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSf1Vl%2FbtsJfb42hYb%2FKgwiNk9asMKeIgQqz3CWYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;358&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;1071&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 ABC의 경우 C의 실패함수는 루트 노드이다. 중요한 점은 ABA이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ABA의 경우 접두사와 접미사가 일치하는 구간이 존재한다.(A)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, KMP 짜듯이 실패함수를 다음과 같이 구축해 준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;1071&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cafDAk/btsJfiv7jq3/1fmCJSkeY7YgvSQ2G0IZxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cafDAk/btsJfiv7jq3/1fmCJSkeY7YgvSQ2G0IZxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cafDAk/btsJfiv7jq3/1fmCJSkeY7YgvSQ2G0IZxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcafDAk%2FbtsJfiv7jq3%2F1fmCJSkeY7YgvSQ2G0IZxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;314&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;1071&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ABAB의 경우 처음 B로 이동하게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 ABABC의 경우가 중요하다. 바로 앞인 ABAB의 실패함수를 따라가면, AB에 도달하게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 위의 AB에도 C가 있고, ABAB에도 C가 있으므로 공통적으로 ABC를 매칭시켜줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 실패함수는 다음과 같이 구축된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;1071&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQGtpy/btsJeOWzeGo/XHAYjO8pUNewJCzPGyYyBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQGtpy/btsJeOWzeGo/XHAYjO8pUNewJCzPGyYyBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQGtpy/btsJeOWzeGo/XHAYjO8pUNewJCzPGyYyBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQGtpy%2FbtsJeOWzeGo%2FXHAYjO8pUNewJCzPGyYyBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;314&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;1071&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라가 보면 알 수 있듯, KMP와 사실상 동일한 방법으로 실패함수를 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 매칭도 KMP와 동일한 방법이라, 설명은 코드로 대체한다.&lt;/p&gt;
&lt;pre id=&quot;code_1724633281327&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void AhoCorasick()
{
  queue&amp;lt;Trie*&amp;gt; q;
  root -&amp;gt; fail = root;
  q.push(root);

  while(!q.empty())
    {
      Trie* cur = q.front();
      q.pop();

      for(int i = 0; i &amp;lt; 26; i++)
        {
          Trie* nxt = cur -&amp;gt; go[i];
          if(!nxt) continue;

          if(cur == root) nxt -&amp;gt; fail = root; //fail(root) = root
          else
          {
            Trie* dest = cur -&amp;gt; fail;
            while(dest != root &amp;amp;&amp;amp; !(dest -&amp;gt; go[i])) dest = dest -&amp;gt; fail;

            if(dest -&amp;gt; go[i] != nullptr) dest = dest -&amp;gt; go[i];
            nxt -&amp;gt; fail = dest;
          }
          if(nxt -&amp;gt; fail -&amp;gt; output) nxt -&amp;gt; output = true;
          q.push(nxt);
        }
    }
}

bool TrieMatch(string str)
{
  Trie* cur = root;
  bool res = false;

  for(int i = 0; i &amp;lt; str.length(); i++)
    {
      int nxt = str[i] - 'a';
      while(cur != root &amp;amp;&amp;amp; !(cur -&amp;gt; go[nxt])) cur = cur -&amp;gt; fail;
      if(cur -&amp;gt; go[nxt]) cur = cur -&amp;gt; go[nxt];

      if(cur -&amp;gt; output)
      {
        res = true;
        break;
      }
    }
  return res;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요하게 보아야 할 것은 output 변수의 관리이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 해당 노드를 끝으로 하는 문자열이 존재하는지를 판별해 주는 변수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시에서는 다음과 같이 색칠된 노드에 output 변수가 true로 되어 있을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;1071&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ejJfkN/btsJeSkpvT1/rhKU1XKyKPsDsbpSReMnb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ejJfkN/btsJeSkpvT1/rhKU1XKyKPsDsbpSReMnb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ejJfkN/btsJeSkpvT1/rhKU1XKyKPsDsbpSReMnb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FejJfkN%2FbtsJeSkpvT1%2FrhKU1XKyKPsDsbpSReMnb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;358&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;1071&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 만약 AB라는 변수가 추가되었다고 해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 일단 이렇게 색칠이 되어 있을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;1071&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ORcA9/btsJe7BDeWz/PDKe1SzsHBfw26fw4Jl9Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ORcA9/btsJe7BDeWz/PDKe1SzsHBfw26fw4Jl9Bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ORcA9/btsJe7BDeWz/PDKe1SzsHBfw26fw4Jl9Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FORcA9%2FbtsJe7BDeWz%2FPDKe1SzsHBfw26fw4Jl9Bk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;358&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;1071&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, 여기 초록색으로 색칠된 부분을 탐색 중이라고 해 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;1071&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FuH87/btsJeQGSljM/yQGfPTNCkWrqqSfeA64B8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FuH87/btsJeQGSljM/yQGfPTNCkWrqqSfeA64B8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FuH87/btsJeQGSljM/yQGfPTNCkWrqqSfeA64B8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFuH87%2FbtsJeQGSljM%2FyQGfPTNCkWrqqSfeA64B8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;358&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;1071&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서부터 내려간다면, 돌아가지 않는 이상 &quot;AB&quot;라는 문자열은 존재하는 지 판별할 수 없다. 적어도 이렇게 색칠되어 있다면.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 우리는 이렇게 AB가 존재하는 것을 알고 있다. 이 점을 해결해 주어야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;1071&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b07LHJ/btsJeDA6aSC/h0In2fbjiRmYjnPZYhZngK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b07LHJ/btsJeDA6aSC/h0In2fbjiRmYjnPZYhZngK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b07LHJ/btsJeDA6aSC/h0In2fbjiRmYjnPZYhZngK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb07LHJ%2FbtsJeDA6aSC%2Fh0In2fbjiRmYjnPZYhZngK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;358&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;1071&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아호-코라식의 경우 문자열 끝만 마킹하지 않고, 일단 해당 지점을 끝으로 하는 문자열이 포함될 수 있다면 모두 마킹한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠시 실패함수의 정의를 생각해보면, 해당 노드를 마지막으로 하는 문자열에 대해서,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 길고 겹치는 접두사와 접미사에 대해 접두사의 맨 끝으로 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 접두사가 마킹이 되어 있다면? 접미사도 겹치므로 해당 노드를 끝으로 하는 부분 문자열도 반드시 존재한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 BFS 탐색이 끝나고, 다음과 같은 과정이 추가로 들어가는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1724634452474&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if(nxt -&amp;gt; fail -&amp;gt; output) nxt -&amp;gt; output = true;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 아호코라식 기초 구현의 예제들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/9250&quot;&gt;9250번: 문자열 집합 판별 (acmicpc.net)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/10256&quot;&gt;10256번: 돌연변이 (acmicpc.net)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Problem Solving</category>
      <author>thisisuserr</author>
      <guid isPermaLink="true">https://thisisuser.tistory.com/72</guid>
      <comments>https://thisisuser.tistory.com/72#entry72comment</comments>
      <pubDate>Mon, 26 Aug 2024 10:08:55 +0900</pubDate>
    </item>
  </channel>
</rss>