#include <iostream>
#include <string>
#include <vector>
#include <stdio.h>
#include <algorithm>
#define _USE_MATH_DEFINES
#include <cmath>
#include <numeric>
#include <iomanip>
#include <deque>
#include <tuple>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <unordered_map>
#include <sstream>
#include <stdexcept>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>

#define rep(i, x) for (int i = 0; i < (int)(x); i++)
#define FOR(i, a, done) for (int i = (a); i < (done); ++i)
#define all(a) (a).begin(), (a).end()
#define F first
#define S second
#define x first
#define y second
#define pb push_back
#define pf push_front
#define debug(a) cout << (a) << endl
namespace mp = boost::multiprecision;
using cpp_int = mp::cpp_int;
using ll = long long;
using namespace std;

ll M = 1000000007;
class UnionFind
{
public:
	vector<ll> par; // 各元の親を表す配列
	vector<ll> siz; // 素集合のサイズを表す配列(1 で初期化)

	// Constructor
	UnionFind(ll sz_) : par(sz_), siz(sz_, 1LL)
	{
		for (ll i = 0; i < sz_; ++i)
			par[i] = i; // 初期では親は自分自身
	}
	void init(ll sz_)
	{
		par.resize(sz_);
		siz.assign(sz_, 1LL); // resize だとなぜか初期化されなかった
		for (ll i = 0; i < sz_; ++i)
			par[i] = i; // 初期では親は自分自身
	}

	// Member Function
	// Find
	ll root(ll x)
	{ // 根の検索
		while (par[x] != x)
		{
			x = par[x] = par[par[x]]; // x の親の親を x の親とする
		}
		return x;
	}

	// Union(Unite, Merge)
	bool merge(ll x, ll y)
	{
		x = root(x);
		y = root(y);
		if (x == y)
			return false;
		// merge technique（データ構造をマージするテク．小を大にくっつける）
		if (siz[x] < siz[y])
			swap(x, y);
		siz[x] += siz[y];
		par[y] = x;
		return true;
	}

	bool issame(ll x, ll y)
	{ // 連結判定
		return root(x) == root(y);
	}

	ll size(ll x)
	{ // 素集合のサイズ
		return siz[root(x)];
	}
};

#define INF 1e+9	 //初期化注意
#define MAX_V 100010 //頂点数の最大値

struct edge
{
	int to;
	int cost;
	//edge e ={to, cost}
};

// <最短距離, 頂点の番号>
using P = pair<int, int>;

int V; // 頂点の数
vector<edge> G[MAX_V];
int d[MAX_V];

void dijkstra(int s)
{
	//sからiへの最短距離をd[i]に保存 i=e.to
	priority_queue<P, vector<P>, greater<P>> que;
	fill(d, d + V, INF);
	d[s] = 0; //初期位置へのコストは0

	que.push(P(0, s));
	//queはmake_pair(cost,to)?

	while (!que.empty())
	{
		P p = que.top();
		que.pop();
		int v = p.second; //v=e.to	from s to v
		//すでにこの経路よりいい経路が見つかっているなら無視
		if (d[v] < p.first)
			continue;

		for (int i = 0; i < G[v].size(); ++i)
		{
			edge e = G[v][i];
			// i 番目の v と v'を結ぶ辺　G=<e.to, e.cost>
			//e.to=v'
			//e.cost means v to v' costs e.cost
			if (d[e.to] > d[v] + e.cost)
			{
				d[e.to] = d[v] + e.cost;
				//from s to v'> from s to v + from v to v'
				//vを経由したほうがコストが低いなら経由する
				que.push(P(d[e.to], e.to));
			}
		}
	}
}

void solve()
{

	int n, m, k;
	cin >> n >> m >> k;
	UnionFind frfr(n);
	vector<int> fr(n);
	vector<int> bl(n);

	rep(i, m)
	{
		int a, b;
		cin >> a >> b;
		a--;
		b--;
		frfr.merge(a, b);
		fr[a]++;
		fr[b]++;
	}
	rep(i, k)
	{
		int c, d;
		cin >> c >> d;
		c--;
		d--;
		if (frfr.issame(c, d))
		{
			bl[c]++;
			bl[d]++;
		}
	}
	vector<int> ans(n);
	rep(i, n)
	{
		ans[i] = frfr.size(i) - fr[i]-bl[i]-1;
	}
	rep(i, n)
	{
		cout << ans[i];
		if (i != n - 1)
			cout << " ";
		else
			cout << endl;
	}
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	solve();
}


Measured memory_bytes: 5888