#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <deque>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <tuple>
#include <vector>

using namespace std;
typedef long long ll;

#define rep(i, n) for (ll i = 0; i < (n); i++)
#define reps(i, f, n) for (ll i = (f); i < (n); i++)
#define repr(i, n) for (ll i = n; i >= 0; i--)
#define repv(v) for (auto it = (v).begin(); it != (v).end(); it++)
#define ALL(x) (x).begin(), (x).end()
#define SZ(x) ((int)(x).size())
#define pb push_back
#define INIT    \
    cin.tie(0); \
    ios::sync_with_stdio(false);

template <class T>
inline bool chmax(T& a, T b) {
    return a = (a < b) ? b : a;
}
template <class T>
inline bool chmin(T& a, T b) {
    return a = (a > b) ? b : a;
}

ll const INF = 1LL << 60;
ll const MOD = 1000000007;

ll const MAX_V = 1000000;

// 辺に属性がある場合
// 頂点toへのコストcostの辺
struct edge {
    ll to, cost;
};
vector<edge> G[MAX_V];

ll d[MAX_V];             // 距離計算
typedef pair<ll, ll> P;  // first: 最短距離, second: 頂点番号

// 探索
vector<bool> seen;
vector<ll> idxs;
bool flg = false;

struct UnionFind {
    vector<int> par;  // par[i]:iの親の番号　(例) par[3] = 2 : 3の親が2

    // 最初は全てが根であるとして初期化
    UnionFind(int N) : par(N) {
        for (int i = 0; i < N; i++) par[i] = i;
    }
    // データxが属する木の根を再帰で得る：root(x) = {xの木の根}
    int root(int x) {
        if (par[x] == x) return x;
        return par[x] = root(par[x]);
    }
    // xとyの木を併合
    void unite(int x, int y) {
        int rx = root(x);  // xの根をrx
        int ry = root(y);  // yの根をry

        // xとyの根が同じ(=同じ木にある)時はそのまま
        if (rx == ry) return;

        // xとyの根が同じでない(=同じ木にない)時：xの根rxをyの根ryにつける
        par[rx] = ry;
    }
    // 2つのデータx, yが属する木が同じならtrueを返す
    bool same(int x, int y) {
        int rx = root(x);
        int ry = root(y);
        return rx == ry;
    }
};

void dfs(int v, int p, int start) {
    seen[v] = true;
    for (auto nv : G[v]) {
        if (nv.to == p) continue;
        if (seen[nv.to]) continue;

        // cout << v << "->" << nv.to << ": " << seen[nv.to] << "," << start
        //      << endl;

        if (start == idxs[nv.to]) {
            flg = true;
            return;
        } else {
            dfs(nv.to, v, start);
        }
    }

    return;
}

int main() {
    INIT;

    ll N, M;
    cin >> N >> M;

    idxs.assign(N + 5, 0);
    rep(i, N) cin >> idxs[i];

    rep(i, N) { idxs[i]--; }

    UnionFind forest(N);

    for (ll i = 0; i < M; i++) {
        ll s, t;
        cin >> s >> t;
        s--;
        t--;

        forest.unite(s, t);

        // edge et = {t, 1};
        // G[s].push_back(et);

        // // 無向グラフの場合はさらにtにsへの辺を張る
        // edge es = {s, 1};
        // G[t].push_back(es);
    }

    ll ans = 0;
    rep(i, N) {
        if (forest.same(i, idxs[i])) ans++;
    }

    cout << ans << endl;

    // seen.assign(N + 5, false);
    // for (ll v = 0; v < N; v++) {
    //     flg = false;
    //     rep(i, N + 5) seen[i] = false;

    //     if (v == idxs[v]) {
    //         flg = true;
    //     } else {
    //         dfs(v, -1, v);
    //     }
    //     // cout << flg << endl;
    //     ans += flg;
    // }

    return 0;
}

Measured memory_bytes: 27768