ykmakuのブログ

競技プログラミングをがんばるブログ

ARC 097 D - Equals

  • 問題

D - Equals

  • 解法

頂点集合が\{1,\dots,N\}、枝集合が\{(x_i,y_i)\}_{i=1}^{M}である無向グラフGを考える。このときGの2つの頂点v,wが同じ連結成分に含まれるならv,wスワップを何回か繰り返すことで値を入れ替える事ができる事がわかる(解説pdf)。よって頂点ip_iが同じ連結成分に含まれているようなiの個数を数えれば良い。これはUnion-findを使って\mathcal{O}(N\log(N))の時間計算量でできる。

#include <iostream>
#include <string>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <queue>
#include <set>
#include <numeric>
#include <cmath>

using namespace std;

typedef long long int ll;

const ll mod = 1e9+7;
const ll INF = 1e9;
const ll MAXN = 1e9;


struct union_find{
  vector<int> par,r;
  int n;

  union_find(){};

  union_find(int x){
    n = x;
    par.resize(n);
    r.resize(n);

    init(n);
  }

  void init(int n){
    for(int i = 0; i < n; i++) par[i] = i;
    for(int i = 0; i < n; i++) r[i] = 0;
  }

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

  void unite(int x,int y){
    x = find(x);
    y = find(y);
    if(x == y){
      return;
    }else if(r[x] < r[y]){
      par[x] = y;
    }else{
      par[y] = x;
      if(r[x] == r[y]) r[x]++;
    }
  }

  bool same(int x,int y){
    return find(x) == find(y);
  }
};

int main()
{
  int n,m;
  cin >> n >> m;
  vector<int> p(n),x(m),y(m);
  for(int i = 0; i < n; i++){
    cin >> p[i];
  }
  for(int i = 0; i < m; i++){
    cin >> x[i] >> y[i];
  }

  union_find uf(n);

  for(int i = 0; i < m; i++){
    uf.unite(x[i],y[i]);
  }

  int sol = 0;
  for(int i = 0; i < n; i++){
    if(uf.same(i+1,p[i])) sol++;
  }

  cout << sol << endl;
  return 0;
}