Notice
Recent Posts
Recent Comments
Link
«   2026/06   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Archives
Today
Total
관리 메뉴

dh-winternagi 님의 블로그

(28277) 뭉쳐야 산다 본문

백준 (C++)/Solve

(28277) 뭉쳐야 산다

dh-winternagi 2026. 4. 23. 19:15

https://www.acmicpc.net/problem/28277

단계별로 풀어보기

43단계(유니온 파인드 2) 1번째

 

 

 

유니온 파인드는 아니고 Small to Large Trick에 대해 알아보는 문제.

집합을 서로 합치는 연산을 할 때 최적화하는 기법이다. N개의 크기 1인 disjoint한 집합을 합치는 연산을 한다고 생각하자. A집합에 B집합을 합치는 연산은 B의 원소 크기에 비례하므로 O(|B|)이다. 이때 항상 작은 집합 B를 큰 집합 A에 합친다고 가정하면 2*|B|≤|A∪B|이므로 항상 원소의 개수가 2배 이상 커지는 집합으로 옮겨가게 된다. 어떤 집합의 원소의 개수의 최댓값은 N개이므로 각 원소는 이동하는 연산을 O(log N)번 이상 수행하지 않으므로 모든 집합을 합쳤을 때 시간복잡도는 O(N log N)이 된다.

만약 집합끼리 disjoint하지 않다고 해도 성립한다. 이 경우 A에만 존재하는 |A-A∩B|개의 원소만 이동 연산을 수행하고 나머지 |A∩B|개의 원소는 이미 B에 있으므로 O(1)의 삭제 연산을 수행한 것이나 마찬가지다. 따라서 A-A∩B 집합과 합치는 연산을 했다고 볼 수 있고, 이는 disjoint하므로 위의 증명이 성립한다.

 

이 문제에서 모든 집합의 원소의 총합은 50만이므로 Small to Large Trick을 쓰면 집합을 합치는 과정을 O(N log N)으로 시간 내에 해낼 수 있다.

 

 

 

#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;

int main() 
{
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);
  
  int n, q;
  
  cin >> n >> q;
  
  vector<set<int>> v(n+1);
  
  for(int i=1;i<=n;i++){
    int sz;
    
    cin >> sz;
    
    vector<int> tmp(sz);
    
    for(int j=0;j<sz;j++)  cin >> tmp[j];
    
    v[i].insert(tmp.begin(), tmp.end());
  }
  
  while(q--){
    int c;
    
    cin >> c;
    
    if(c==1){
      int a, b;
      
      cin >> a >> b;
      
      if(v[a].size()<v[b].size()) swap(v[a],v[b]);
      
      v[a].insert(v[b].begin(), v[b].end());
      v[b].clear();
    }else{
      int a;
      
      cin >> a;
      
      cout << v[a].size() << "\n";
    }
  }
  
  return 0;
}

'백준 (C++) > Solve' 카테고리의 다른 글

(17469) 트리의 색깔과 쿼리  (0) 2026.04.23
(13306) 트리  (0) 2026.04.23
(17082) 쿼리와 쿼리  (0) 2026.04.23
(12456) 모닝커피 (Large)  (0) 2026.04.23
(12776) Swap Space  (0) 2026.04.23