BOJ 20156 - 기왕 이렇게 된 거 암기왕이 되어라

image.png

문제를 읽고 Offline Query + DSU 임은 자명해보였으나 엣지 케이스를 고려하지 못했다.

상당히 입력이 자유롭게 들어와서 입력도 잘 처리해줘야 하는 문제이고 엣지 케이스도 있다.

$x$ 가 부모가 있다고 할 때 $t=2$ 에서 제거가 되었다면 $t=5$ 에서 $x$ 가 다시 제거되어도 그건 무시해줘야 한다.

왜냐면 이미 $t=2$ 에서 부모가 제거가 되어있기 때문에 $t=5$ 에서는 부모가 없는 분리된 트리의 루트인 상태이기 때문이다.

또한 처음에 부모-자식 관계가 끝까지 끊어지지 않는다면 처음에 DSU에서 모두 이어주고 시작해야 함을 유의하자.

나머지는 어렵지 않다. 쿼리를 $t$ 를 내림차순으로 정렬하고, 하나하나 보며 분리의 역은 merge 이므로 DSU에서 현재 쿼리의 시점보다 뒤인 것들은 모두 merge 시켜주고 DSU에서 이어져있는지를 판단하면 된다.

void solve() {  
   int n, m, k;  
   cin >> n >> m >> k;  
   DSU dsu(n);  
   vi par(n);  
   for (int i = 0; i < n; i++) {  
      cin >> par[i];  
      if (par[i] != -1) par[i]--;  
      assert(i != par[i]);  
   }  
   vi disconnect(m + 1), removed(n);  
   for (int i = 0; i < m; i++) {  
      int d;  
      cin >> d;  
      d--;  
      if (par[d] != -1 && removed[d] == 0) {  
         removed[d] = 1;  
         disconnect[i + 1] = d;  
      } else {  
         disconnect[i + 1] = -1;  
      }  
   }  
   for (int i = 0; i < n; i++) {  
      if (!removed[i] && par[i] != -1) {  
         debug(i, par[i]);  
         dsu.merge(par[i], i);  
      }  
   }  
   debug(disconnect);  
  
   vi ans(k);  
   vector<array<int, 4>> qry(k);  
   for (int i = 0; i < k; i++) {  
      cin >> qry[i][0] >> qry[i][1] >> qry[i][2];  
      qry[i][3] = i;  
      qry[i][1]--;  
      qry[i][2]--;  
   }  
   sort(all(qry), [&](auto &a, auto &b) {  
      return a[0] > b[0];  
   });  
   // 1 2 3  
   //   d  qry  
   for (int i = 0, j = m; i < k; i++) {  
      while (j >= 1 && j > qry[i][0]) {  
         int x = disconnect[j];  
         if (x != -1) {  
            dsu.merge(par[x], x);  
         }  
         j--;  
      }  
      if (dsu.is_merged(qry[i][1], qry[i][2])) ans[qry[i][3]] = 1;  
   }  
   for (int i = 0; i < k; i++) {  
      if (ans[i]) cout << "Same Same;\n";  
      else cout << "No;\n";  
   }  
}

Tags:

Categories:

Updated:

Comments