#include <bits/stdc++.h>
using namespace std;
template <class T1, class T2>
ostream &operator<<(ostream &s, pair<T1, T2> P) {
  return s << '<' << P.first << ", " << P.second << '>';
}
template <class T>
ostream &operator<<(ostream &s, vector<T> P) {
  for (int i = 0; i < P.size(); ++i) {
    if (i > 0) {
      s << " ";
    }
    s << P[i];
  }
  return s;
}
template <class T>
ostream &operator<<(ostream &s, vector<vector<T>> P) {
  for (int i = 0; i < P.size(); ++i) {
    s << endl << P[i];
  }
  return s << endl;
}
template <class T>
ostream &operator<<(ostream &s, set<T> P) {
  for (auto it : P) {
    s << "<" << it << "> ";
  }
  return s << endl;
}
template <class T1, class T2>
ostream &operator<<(ostream &s, map<T1, T2> P) {
  for (auto it : P) {
    s << "<" << it.first << "->" << it.second << "> ";
  }
  return s << endl;
}
struct UnionFind {
  vector<int> par;
  stack<pair<int, int>> history;
  UnionFind() {}
  UnionFind(int n) : par(n, -1) {}
  void init(int n) { par.assign(n, -1); }
  int root(int x) {
    if (par[x] < 0)
      return x;
    else
      return root(par[x]);
  }
  bool issame(int x, int y) { return root(x) == root(y); }
  bool merge(int x, int y) {
    x = root(x);
    y = root(y);
    history.emplace(x, par[x]);
    history.emplace(y, par[y]);
    if (x == y) return false;
    if (par[x] > par[y]) swap(x, y);
    par[x] += par[y];
    par[y] = x;
    return true;
  }
  int size(int x) { return -par[root(x)]; }
  void undo() {
    for (int iter = 0; iter < 2; ++iter) {
      par[history.top().first] = history.top().second;
      history.pop();
    }
  }
  void snapshot() {
    while (!history.empty()) history.pop();
  }
  void rollback() {
    while (!history.empty()) undo();
  }
};
ostream &operator<<(ostream &s, UnionFind uf) {
  map<int, vector<int>> res;
  for (int i = 0; i < uf.par.size(); ++i) {
    int r = uf.root(i);
    res[r].push_back(i);
  }
  for (map<int, vector<int>>::iterator it = res.begin(); it != res.end();
       ++it) {
    s << endl;
    for (int j = 0; j < (int)it->second.size(); ++j) {
      s << it->second[j] << ", ";
    }
  }
  return s << endl;
}
using pint = pair<int, int>;
using Graph = vector<vector<int>>;
long long CF680DIV1C(int N, int M, int K, const vector<int> &C, const Graph &G,
                     UnionFind &uf) {
  uf.snapshot();
  vector<bool> isbi(K, true);
  long long con = K;
  for (int v = 0; v < N; ++v) {
    if (!isbi[C[v]]) continue;
    if (uf.issame(v, v + N)) --con, isbi[C[v]] = false;
  }
  long long res = con * (con - 1) / 2;
  map<pint, vector<pint>> ma;
  for (int v = 0; v < N; ++v) {
    for (auto u : G[v]) {
      if (!isbi[C[v]] || !isbi[C[u]] || C[v] == C[u]) continue;
      ma[pint(min(C[v], C[u]), max(C[v], C[u]))].push_back(pint(v, u));
    }
  }
  for (auto it : ma) {
    bool ok = true;
    for (auto e : it.second) {
      int u = e.first, v = e.second;
      uf.merge(u, v + N), uf.merge(u + N, v);
      if (uf.issame(u, u + N) || uf.issame(v, v + N)) ok = false;
    }
    if (!ok) --res;
    uf.rollback();
  }
  return res;
}
int main() {
  cin.tie(0);
  ios::sync_with_stdio(false);
  int N, M, K;
  cin >> N >> M >> K;
  vector<int> C(N);
  for (int i = 0; i < N; ++i) cin >> C[i], --C[i];
  Graph G(N);
  UnionFind uf(N * 2);
  for (int i = 0; i < M; ++i) {
    int u, v;
    cin >> u >> v;
    --u, --v;
    G[u].push_back(v), G[v].push_back(u);
    if (C[u] == C[v]) uf.merge(u, v + N), uf.merge(u + N, v);
  }
  cout << CF680DIV1C(N, M, K, C, G, uf) << endl;
}
