#include<bits/stdc++.h>
#include<time.h>
#define llu long long unsigned
#define MAXSIZE 10000
#define pb push_back
using namespace::std;
typedef vector <int> vi;
typedef pair < int, int > ii;
typedef vector < ii > vii;
typedef vector <vii> vvii;
typedef vector < vi > vvi;
//r2 keeps track of the no. of open window children of root of /**each**/ tree in the forest
map <int, llu> r2;
map <int, llu> aux_map;
vi tree;
vector <int> track;
vector <bool> vs;
//class disjoint_sets
//{
// public :
//
// unordered_map <int, int > parent;
// unordered_map <int, int > rank;
//
// //constructor
// disjoint_sets(deque <int> &universe)
// {
// for(int x : universe)//for(auto it = universe.begin() ; it != universe.end() ; ++it) //: line no nodes req
// {
// parent[x] = x;
// rank[x] = 0;
// }
// }
// int find(int item)
// {
// if(parent[item] == item)
// return item;
// else return find(parent[item]);
// }
// void unite(int a, int b)
// {
// int a_parent = find(a);
// int b_parent = find(b);
// if(rank[a_parent] > rank[b_parent])
// {
// parent[b_parent] = a_parent;
// rank[a_parent] += rank[b_parent];
// }
// else if(rank[a_parent] < rank[b_parent])
// {
// parent[a_parent] = b_parent;
// rank[b_parent] += rank[a_parent];
// }
// else
// {
// parent[a_parent] = b_parent;
// rank[b_parent] ++;
// }
// }
// void disp()
// {
// for(pair < int, int > p : parent) //for(auto it = parent.begin() ; it != parent.end() ; ++it)
// cout<<p.second<<" ";
// }
//};
int dfs_fun(vvi &g, int x, vector <int> &key, vector <bool> &vs) //function for second query
{
vs[x] = true;
int res = (key[x] == 1) ? 1 :0;
for(auto it : g[x])
if(!vs[it])
{
if(key[it] == 1)
{
vs[it] = true;
return 1;
}
res = dfs_fun(g, it, key, vs);
if(res) return res;
}
return res;
}
llu dfs(vvi &g, vector <bool> &vs, int v, vi &key)
{
vs[v] = true;
llu c = 1; //contribution of the node on which dfs has been called, ...we'll return c-1 later if this nodes window is closed
for(auto it = g[v].begin() ; it != g[v].end() ; ++it)
if(!vs[*it])
c += dfs(g, vs, *it, key); //increment c by the no of open window nodes rooted at c
if(key[v]) {
r2[v] = c ; //map r2 keeps track of no. of open window nodes rooted at each node of the graph
return c ;
}
{
r2[v] = c-1 ; //if the node has its window closed,adjust its contribution which was initiated as 1
return c-1;
}
}
void drought(vvi &g, vi &key, int V, vi &r1)
{
vs = vector<bool> (V+1, false);
vs[0] = true;
for(int i = 1 ; i <= V ; ++i)
if(!vs[i])
{
tree.pb(i); // the vector tree keeps track of root of each tree in the forest
r1.pb(dfs(g, vs, i, key)); //the vector r1 stores the no of 'open window' nodes rooted at each root of the forest including the status of root
}
}
long long unsigned calculate(vi tree, map<int, llu> &r2)
{
long long unsigned count = 0;
for(auto it : tree)
count += (r2[it]*(r2[it]-1))/2;
return count;
}
int main()
{
// ios_base::sync_with_stdio(0);
// cin.tie(0);
srand(time(0));
track.reserve(50050);
tree.reserve(50050);
int V, E, s, d, w, st, auxi = 0;
//cout<<"\nenter the no of nodes and edges :\t ";
cin>>V>>E;
//deque <int> aux;
// for(int i = 0; i < V ; ++i) aux.pb(i+1);
// disjoint_sets dso(aux);
for(int i = 0 ; i <= V ; ++i) r2[i] = 0;
tree.pb(0);
vi r1;
vi key(V+1);
vvi g(V+1);
//cout<<"\nenter the window status \n";
key[0] = 0;
for(int i = 1 ; i <= V ; ++i)
{
//st = rand()%2;
cin>>st;
key[i] = st;
}
/**cout<<"\nutilising rand(), the window status and links bw friends that have been formed are :\n";**/
// for(int i = 1 ; i <= V ; ++i)
// cout<<key[i]<<' ';
//cout<<"\nenter each edge\n";
for(int i = 0 ; i < E ;++i)
{
cin>>s;
cin>>d;
g[s].pb(d);
g[d].pb(s);
}
drought(g, key, V, r1);
// cout<<"\nno of open window children rooted @ a prticular node(including its own status) : node -> open window children "<<endl;
// cout<<"\nigonre the case for 0"<<endl;
// for(auto it : r2)
// cout<<it.first<<" -> "<<it.second<<endl;
///****************************************************************************************************************************/
//cout<<"\nanswer to query 1 is : \t";
cout<<calculate(tree, r2)<<' ';
tree.pb(V+1); // last element of the vector tree contains V+1 to make the do while loop (to find answer to query 2)ahead functional
auto it1 = tree.begin()+1; //root of the very first tree
auto it2 = it1+1; //root of the next tree
llu rubik = 0;
vector <int> remaining;
// cout<<"\n3 cases for second answer :";
// cout<<"\n1.element has at least 1 open window child also there is some open window node rooted at the same root at which the element is hence at least the way between these 2 nodes connects 'em";
// cout<<"\n2.element has its window open and has at least one open window child, hence the path bw them connects 'em";
// cout<<"\n3.else the node must have at least two open window nodes rooted @ itself";
while(*it1 != V+1) //for root of each tree in the forest do the following
{
llu x = *it1 ; //first element of the present tree
llu root_id = r2[x]; //no. of open window nodes rooted @ node x
while( x < *it2 ) //for each element of the tree rooted at it1 do the following
{
if(r2[x] > 0 && r2[x] < root_id) //element has at least 1 open window child also there is some open window node rooted at the same root at which the element is hence at least the way between these 2 nodes connects 'em
{rubik++;} //cout<<"\t 1. "<<x<<endl;}
else {
if(key[x] == 1 && r2[x] > 1) //element has its window open and has at least one open window child, hence the path bw them connects 'em
{rubik++;} //cout<<"\t 2. "<<x<<endl;}
else //else for the element must have at least two open window nodes rooted @ itself
remaining.pb(x);
} //store these elements in a vector, w'll deal with those later, this requires another loop
x++;
}
it1++; /*increment the tree*/
it2++;
}
// for(auto it : remaining)
// cout<<"\n"<<"3. "<<it<<endl;
for(auto it : remaining)
{
vs = vector<bool> (V+1, false);
track = vector<int> (V+1, 0);
int i = 0;
while(count(begin(track), end(track), 1)<2 && i < g[it].size()) //loop till either we find at least 2 open window children rooted at concerned node 'x' (lying in remaining) or we visit all adj nodes of x
{
track.pb(dfs_fun(g, it, key, vs));
i++;
}
if(count(begin(track), end(track), 1)>=2) {rubik++;} //cout<<"\n"<<it<<" contributes towards the second answer";}
}
//cout<<"the result of query 2 is :\t";
cout<<rubik<<endl;
return 0;
}