Problem: F - Point Distance

Problem Statement

You work for invention center as a part time programmer. This center researches movement of protein molecules. It needs how molecules make clusters, so it will calculate distance of all pair molecules and will make a histogram. Molecules’ positions are given by a N x N grid map. Value C_{xy} of cell (x, y) in a grid map means that C_{xy} molecules are in the position (x, y).

You are given a grid map, please calculate histogram of all pair molecules.

Input

Input is formatted as follows.


N
C_{11} C_{12} ... C_{1N}
C_{21} C_{22} ... C_{2N}
...
C_{N1} C_{N2} ... C_{NN}


First line contains the grid map size N (1 \leq N \leq 1024). Each next N line contains N numbers. Each numbers C_{xy} (0 \leq C_{xy} \leq 9) means the number of molecule in position (x,y). There are at least 2 molecules.

Output

Output is formatted as follows.


D_{ave}
d_{1} c_{1}
d_{2} c_{2}
...
d_{m} c_{m}


Print D_{ave} which an average distance of all pair molecules on first line. Next, print histogram of all pair distance. Each line contains a distance d_i and the number of pair molecules c_i(0 \lt c_i). The distance d_i should be calculated by squared Euclidean distance and sorted as increasing order. If the number of different distance is more than 10,000, please show the first 10,000 lines. The answer may be printed with an arbitrary number of decimal digits, but may not contain an absolute or relative error greater than or equal to 10^{-8}.

Sample Input 1


2
1 0
0 1


Output for the Sample Input 1


1.4142135624
2 1


Sample Input 2


3
1 1 1
1 1 1
1 1 1


Output for the Sample Input 2


1.6349751825
1 12
2 8
4 6
5 8
8 2


Sample Input 3


5
0 1 2 3 4
5 6 7 8 9
1 2 3 2 1
0 0 0 0 0
0 0 0 0 1


Output for the Sample Input 3


1.8589994382
0 125
1 379
2 232
4 186
5 200
8 27
9 111
10 98
13 21
16 50
17 37
18 6
20 7
25 6






Example

Input

2
1 0
0 1


Output

1.4142135624
2 1

Solution (in CPP):

Solution:
#include <vector>
#include <iostream>
#include <utility>
#include <algorithm>
#include <string>
#include <deque>
#include <tuple>
#include <queue>
#include <functional>
#include <cmath>
#include <iomanip>
#include <map>
#include <numeric>
#include <unordered_map>
#include <unordered_set>
#include <complex>
#include <iterator>
#include <array>
#include <chrono>
//cin.sync_with_stdio(false);
//streambuf
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
using vi = vector<int>;
using vll = vector<ll>;
using vpii = vector<pii>;
template<class T, int s>using va = vector<array<T, s>>;
template<class T, class T2> using umap = unordered_map<T, T2>;
template<class T> using uset = unordered_set<T>;
template<class T, class S> void cmin(T &a, const S b) { if (a > b)a = b; }
template<class T, class S> void cmax(T &a, const S b) { if (a < b)a = b; }
#define ALL(a) a.begin(),a.end()
#define rep(i,a) for(int i=0;i<a;i++)
#define rep1(i,a) for(int i=1;i<=a;i++)
#define rrep(i,a) for(int i=a-1;i>=0;i--)
#define rrep1(i,a) for(int i=a;i;i--)
const ll mod = 1000000007;
#ifndef INT_MAX
const int INT_MAX = numeric_limits<signed>().max();
#endif
template<class T>using heap = priority_queue<T, vector<T>, greater<T>>;
template<class T>using pque = priority_queue<T, vector<T>, function<T(T, T)>>;
template <class T>
inline void hash_combine(size_t & seed, const T & v) {
	hash<T> hasher;
	seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
namespace std {

	template<typename S, typename T> struct hash<pair<S, T>> {
		inline size_t operator()(const pair<S, T> & v) const {
			size_t seed = 0;
			hash_combine(seed, v.first);
			hash_combine(seed, v.second);
			return seed;
		}
	};
	// Recursive template code derived from Matthieu M.
	template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1>
	struct HashValueImpl
	{
		static void apply(size_t& seed, Tuple const& tuple)
		{
			HashValueImpl<Tuple, Index - 1>::apply(seed, tuple);
			hash_combine(seed, std::get<Index>(tuple));
		}
	};

	template <class Tuple>
	struct HashValueImpl<Tuple, 0>
	{
		static void apply(size_t& seed, Tuple const& tuple)
		{
			hash_combine(seed, std::get<0>(tuple));
		}
	};

	template <typename ... TT>
	struct hash<std::tuple<TT...>>
	{
		size_t
			operator()(std::tuple<TT...> const& tt) const
		{
			size_t seed = 0;
			HashValueImpl<std::tuple<TT...> >::apply(seed, tt);
			return seed;
		}

	};
}
ll pow(ll base, ll i, ll mod) {
	ll a = 1;
	while (i) {
		if (i & 1) {
			a *= base;
			a %= mod;
		}
		base *= base;
		base %= mod;
		i /= 2;
	}
	return a;
}
class unionfind {
	vector<int> par, rank, size_;//????????§??????????????¢???????????????????????????rank???????????????size?????????
public:
	unionfind(int n) :par(n), rank(n), size_(n, 1) {
		iota(ALL(par), 0);
	}
	int find(int x) {
		if (par[x] == x)return x;
		return par[x] = find(par[x]);
	}
	void unite(int x, int y) {
		x = find(x), y = find(y);
		if (x == y)return;
		if (rank[x] < rank[y])swap(x, y);
		par[y] = x;
		size_[x] += size_[y];
		if (rank[x] == rank[y])rank[x]++;
	}
	bool same(int x, int y) {
		return find(x) == find(y);
	}
	int size(int x) {
		return size_[find(x)];
	}
};
ll gcd(ll a, ll b) {
	while (b) {
		ll c = a%b;
		a = b;
		b = c;
	}
	return a;
}
ll lcm(ll a, ll b) {
	return a / gcd(a, b)*b;
}
int popcnt(unsigned long long a) {
	a = (a & 0x5555555555555555) + (a >> 1 & 0x5555555555555555);
	a = (a & 0x3333333333333333) + (a >> 2 & 0x3333333333333333);
	a = (a & 0x0f0f0f0f0f0f0f0f) + (a >> 4 & 0x0f0f0f0f0f0f0f0f);
	a = (a & 0x00ff00ff00ff00ff) + (a >> 8 & 0x00ff00ff00ff00ff);
	a = (a & 0x0000ffff0000ffff) + (a >> 16 & 0x0000ffff0000ffff);
	return (a & 0xffffffff) + (a >> 32);
}
template<class T, class Func = function<T(T, T)>>
class segtree {
	vector<T> obj;
	int offset;
	Func updater;
	T e;
	int bufsize(int num) {
		int i = 1;
		for (; num >i; i <<= 1);
		offset = i - 1;
		return (i << 1) - 1;
	}
	T query(int a, int b, int k, int l, int r) {
		if (r <= a || b <= l)return e;
		if (a <= l && r <= b)return obj[k];
		else return updater(query(a, b, k * 2 + 1, l, (l + r) / 2), query(a, b, k * 2 + 2, (l + r) / 2, r));
	}
public:
	T query(int a, int b) {//[a,b)
		return query(a, b, 0, 0, offset + 1);
	}
	void updateall(int l = 0, int r = -1) {
		if (r < 0)r = offset + 1;
		l += offset, r += offset;
		do {
			l = l - 1 >> 1, r = r - 1 >> 1;
			for (int i = l; i < r; i++)obj[i] = updater(obj[i * 2 + 1], obj[i * 2 + 2]);
		} while (l);
	}
	void update(int k, T &a) {
		k += offset;
		obj[k] = a;
		while (k) {
			k = k - 1 >> 1;
			obj[k] = updater(obj[k * 2 + 1], obj[k * 2 + 2]);
		}
	}
	segtree(int n, T e, const Func &updater_ = Func()) :obj(bufsize(n), e), e(e), updater(updater_) {}
	segtree(vector<T> &vec, T e, const Func &updater = Func()) :obj(bufsize(vec.size()), e), e(e), updater(updater) {
		copy(vec.begin(), vec.end(), obj.begin() + offset);
		updateall();
	}
	typename vector<T>::reference operator[](int n) {
		return obj[n + offset];
	}
};
template<class T>
class matrix {
	vector<vector<T>> obj;
	pair<int, int> s;
public:
	matrix(pair<int, int> size, T e = 0) :matrix(size.first, size.second, e) {}
	matrix(int n, int m = -1, T e = 0) :obj(n, vector<T>(m == -1 ? n : m, e)), s(n, m == -1 ? n : m) {}
	static matrix e(int n) {
		matrix a = (n);
		for (int i = 0; i < n; i++)a[i][i] = 1;
		return a;
	}
	matrix& operator+=(const matrix &p) {
		if (s != p.s)throw runtime_error("matrix error");
		for (int i = 0; i < s.first; i++)
			for (int j = 0; j < s.second; j++)obj[i][j] += p.obj[i][j];
		return *this;
	}
	matrix operator+(const matrix &p) {
		matrix res(*this);
		return res += p;
	}
	matrix& operator-=(const matrix &p) {
		if (s != p.s)throw runtime_error("matrix error");
		for (int i = 0; i < s.first; i++)
			for (int j = 0; j < s.second; j++)obj[i][j] -= p.obj[i][j];
		return *this;
	}
	matrix operator-(const matrix &p) {
		matrix res(*this);
		return res -= p;
	}
	matrix& operator*=(T p) {
		for (auto &a : obj)
			for (auto &b : a)b *= p;
		return *this;
	}
	matrix operator*(T p) {
		matrix res(*this);
		return res *= p;
	}
	matrix operator*(const matrix &p) {
		if (s.second != p.s.first)throw runtime_error("matrix error");
		matrix ret(s.first, p.s.second);
		for (int i = 0; i < s.first; i++)
			for (int j = 0; j < s.second; j++)
				for (int k = 0; k < p.s.second; k++)ret[i][k] += obj[i][j] * p.obj[j][k];
		return ret;
	}
	matrix &operator*=(const matrix &p) {
		return *this = *this*p;
	}
	pair<int, int> size() const {
		return s;
	}
	matrix &mod(T m) {
		for (auto &a : obj)
			for (auto &b : a)b %= m;
		return *this;
	}
	typename vector<vector<T>>::reference operator[](int t) {
		return obj[t];
	}
};
template<class T> inline
matrix<T> pow(matrix<T> &base, unsigned exp) {
	auto base_(base);
	if (base_.size().first != base_.size().second)throw runtime_error("matrix error");
	matrix<T> res = matrix<T>::e(base_.size().first);
	for (;;) {
		if (exp & 1)res *= base_;
		if (!(exp /= 2))break;
		base_ *= base_;
	}
	return res;
}
template<class T> inline
matrix<T> modpow(matrix<T> &base, unsigned exp, T m) {
	auto base_(base);
	if (base.size().first != base_.size().second)throw runtime_error("matrix error");
	matrix<T> res = matrix<T>::e(base_.size().first);
	for (;;) {
		if (exp & 1)(res *= base_).mod(m);
		if (!(exp /= 2))break;
		(base_ *= base_).mod(m);
	}
	return res;
}
template<class T>int id(vector<T> &a, T b) {
	return lower_bound(ALL(a), b) - a.begin();
}
class Flow {
	int V;
	struct edge { int to, cap, rev, cost; };
	vector<vector<edge>> G;
	vector<int> level, iter, h, dist, prevv, preve;
	Flow(int size) :G(size + 1), V(size + 1) {
	}
	void add_edge(int from, int to, int cap, int cost = 0) {
		G[from].push_back(edge{ to, cap, (int)G[to].size(),cost });
		G[to].push_back(edge{ from,0,(int)G[from].size() - 1,-cost });
	}
	void bfs(int s) {
		fill(level.begin(), level.end(), -1);
		queue<int> que;
		level[s] = 0;
		que.push(s);
		while (!que.empty()) {
			int v = que.front(); que.pop();
			for (int i = 0; i < G[v].size(); i++) {
				edge &e = G[v][i];
				if (e.cap > 0 && level[e.to] < 0) {
					level[e.to] = level[v] + 1;
					que.push(e.to);
				}
			}
		}
	}
	int dfs(int v, int t, int f) {
		if (v == t)return f;
		for (int &i = iter[v]; i < G[v].size(); i++) {
			edge &e = G[v][i];
			if (e.cap > 0 && level[v] < level[e.to]) {
				int d = dfs(e.to, t, min(f, e.cap));
				if (d > 0) {
					e.cap -= d;
					G[e.to][e.rev].cap += d;
					return d;
				}
			}
		}
		return 0;
	}
	int max_flow(int s, int t) {
		level.resize(V);
		iter.resize(V);
		int flow = 0;
		for (;;) {
			bfs(s);
			if (level[t] < 0)return flow;
			fill(iter.begin(), iter.end(), 0);
			int f;
			while ((f = dfs(s, t, numeric_limits<int>::max()))>0) {
				flow += f;
			}
		}
	}
	typedef pair<int, int> P;
	int min_cost_flow(int s, int t, int f) {
		int res = 0;
		h.resize(V);
		dist.resize(V);
		prevv.resize(V);
		preve.resize(V);
		fill(h.begin(), h.end(), 0);
		while (f > 0) {
			priority_queue<P, vector<P>, greater<P>> que;
			fill(dist.begin(), dist.end(), numeric_limits<int>::max());
			dist[s] = 0;
			que.push({ 0,s });
			while (!que.empty()) {
				P p = que.top(); que.pop();
				int v = p.second;
				if (dist[v] < p.first)continue;
				for (int i = 0; i < G[v].size(); i++) {
					edge &e = G[v][i];
					if (e.cap > 0 && dist[e.to]>dist[v] + e.cost + h[v] - h[e.to]) {
						dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
						prevv[e.to] = v;
						preve[e.to] = i;
						que.push({ dist[e.to],e.to });
					}
				}
			}
			if (dist[t] == numeric_limits<int>::max()) {
				return -1;
			}
			for (int v = 0; v < V; v++)h[v] += dist[v];

			int d = f;
			for (int v = t; v != s; v = prevv[v]) {
				d = min(d, G[prevv[v]][preve[v]].cap);
			}
			f -= d;
			res += d*h[t];
			for (int v = t; v != s; v = prevv[v]) {
				edge &e = G[prevv[v]][preve[v]];
				e.cap -= d;
				G[v][e.rev].cap += d;
			}
		}
		return res;
	}
};
const ld eps = 1e-11, pi = acos(-1.0);
typedef complex<ld> P;
typedef vector<P> VP;
ld dot(P a, P b) { return real(conj(a) * b); }
ld cross(P a, P b) { return imag(conj(a) * b); }

namespace std {
	bool operator<(const P &a, const P &b) {
		return abs(a.real() - b.real()) < eps ? a.imag() < b.imag() : a.real() < b.real();
	}
}

struct L { P a, b; };//line->l,segment->s
struct C { P p; ld r; };

int ccw(P a, P b, P c) {
	b -= a; c -= a;
	if (cross(b, c) > eps) return 1;   // counter clockwise
	if (cross(b, c) < -eps) return -1; // clockwise
	if (dot(b, c) < 0) return 2;       // c--a--b on line
	if (norm(b) < norm(c)) return -2;  // a--b--c on line
	return 0;                          // a--c--b on line
}

bool isis_ll(L l, L m) {//is intersect
	return abs(cross(l.b - l.a, m.b - m.a)) > eps;
}

bool isis_ls(L l, L s) {
	ld a = cross(l.b - l.a, s.a - l.a);
	ld b = cross(l.b - l.a, s.b - l.a);
	return (a * b < eps);
}

bool isis_lp(L l, P p) {
	return abs(cross(l.b - p, l.a - p)) < eps;
}

bool isis_ss(L s, L t) {
	return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0 &&
		ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0;
}

P is_ll(L s, L t) { //intersect
	P sv = s.b - s.a, tv = t.b - t.a;
	return s.a + sv * cross(tv, t.a - s.a) / cross(tv, sv);
}

bool isis_sp(L s, P p) {
	return abs(s.a - p) + abs(s.b - p) - abs(s.b - s.a) < eps;
}

P proj(L l, P p) {
	ld t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
	return l.a + t * (l.a - l.b);
}

ld dist_lp(L l, P p) {
	return abs(p - proj(l, p));
}

ld dist_ll(L l, L m) {
	return isis_ll(l, m) ? 0 : dist_lp(l, m.a);
}

ld dist_ls(L l, L s) {
	if (isis_ls(l, s)) return 0;
	return min(dist_lp(l, s.a), dist_lp(l, s.b));
}

ld dist_sp(L s, P p) {
	P r = proj(s, p);
	if (isis_sp(s, r)) return abs(r - p);
	return min(abs(s.a - p), abs(s.b - p));
}

ld dist_ss(L s, L t) {
	if (isis_ss(s, t)) return 0;
	ld a = min(dist_sp(s, t.a), dist_sp(t, s.a));
	ld b = min(dist_sp(s, t.b), dist_sp(t, s.b));
	return min(a, b);
}

VP is_cc(C c1, C c2) {
	VP res;
	ld d = abs(c1.p - c2.p);
	ld rc = (d * d + c1.r * c1.r - c2.r * c2.r) / (2 * d);
	ld dfr = c1.r * c1.r - rc * rc;
	if (abs(dfr) < eps) dfr = 0.0;
	else if (dfr < 0.0) return res; // no intersection
	ld rs = sqrt(dfr);
	P diff = (c2.p - c1.p) / d;
	res.push_back(c1.p + diff * P(rc, rs));
	if (dfr != 0.0) res.push_back(c1.p + diff * P(rc, -rs));
	return res;
}

bool isis_vc(vector<C> vc) {
	VP crs;
	int n = vc.size();
	rep(i, n)rep(j, i)
		for (P p : is_cc(vc[i], vc[j]))
			crs.push_back(p);
	rep(i, n)
		crs.push_back(vc[i].p);
	for (P p : crs) {
		bool valid = true;
		rep(i, n)
			if (abs(p - vc[i].p)>vc[i].r + eps)
				valid = false;
		if (valid) return true;
	}
	return false;
}

VP is_lc(C c, L l) {
	VP res;
	ld d = dist_lp(l, c.p);
	if (d < c.r + eps) {
		ld len = (d > c.r) ? 0.0 : sqrt(c.r * c.r - d * d); //safety;
		P nor = (l.a - l.b) / abs(l.a - l.b);
		res.push_back(proj(l, c.p) + len * nor);
		res.push_back(proj(l, c.p) - len * nor);
	}
	return res;
}

VP is_sc(C c, L l) {
	VP v = is_lc(c, l), res;
	for (P p : v)
		if (isis_sp(l, p)) res.push_back(p);
	return res;
}

vector<L> tangent_cp(C c, P p) {//????????\????
	vector<L> ret;
	P v = c.p - p;
	ld d = abs(v);
	ld l = sqrt(norm(v) - c.r * c.r);
	if (isnan(l)) { return ret; }
	P v1 = v * P(l / d, c.r / d);
	P v2 = v * P(l / d, -c.r / d);
	ret.push_back(L{ p, p + v1 });
	if (l < eps) return ret;
	ret.push_back(L{ p, p + v2 });
	return ret;
}

vector<L> tangent_cc(C c1, C c2) {
	vector<L> ret;
	if (abs(c1.p - c2.p) - (c1.r + c2.r) > -eps) {
		P center = (c1.p * c2.r + c2.p * c1.r) / (c1.r + c2.r);
		ret = tangent_cp(c1, center);
	}
	if (abs(c1.r - c2.r) > eps) {
		P out = (-c1.p * c2.r + c2.p * c1.r) / (c1.r - c2.r);
		vector<L> nret = tangent_cp(c1, out);
		ret.insert(ret.end(), ALL(nret));
	}
	else {
		P v = c2.p - c1.p;
		v /= abs(v);
		P q1 = c1.p + v * P(0, 1) * c1.r;
		P q2 = c1.p + v * P(0, -1) * c1.r;
		ret.push_back(L{ q1, q1 + v });
		ret.push_back(L{ q2, q2 + v });
	}
	return ret;
}

ld area(const VP &p) {//??¢?????
	ld res = 0;
	int n = p.size();
	rep(j, n) res += cross(p[j], p[(j + 1) % n]);
	return res / 2;
}

bool is_polygon(L l, VP &g) {
	int n = g.size();
	for (int i = 0; i < n; i++) {
		P a = g[i];
		P b = g[(i + 1) % n];
		if (isis_ss(l, L{ a, b })) return true;
	}
	return false;
}

int is_in_Polygon(const VP &g, P p) {
	bool in = false;
	int n = g.size();
	for (int i = 0; i < n; i++) {
		P a = g[i] - p, b = g[(i + 1) % n] - p;
		if (imag(a) > imag(b)) swap(a, b);
		if (imag(a) <= 0 && 0 < imag(b))
			if (cross(a, b) < 0) in = !in;
		if (abs(cross(a, b)) < eps && dot(a, b) < eps) return 0; // on
	}
	if (in) return 1; // in
	return -1; // out
}

VP ConvexHull(VP ps) {
	int n = ps.size();
	int k = 0;
	sort(ps.begin(), ps.end());
	VP ch(2 * n);
	for (int i = 0; i < n; ch[k++] = ps[i++])
		while (k >= 2 && ccw(ch[k - 2], ch[k - 1], ps[i]) <= 0) --k;
	for (int i = n - 2, t = k + 1; i >= 0; ch[k++] = ps[i--])
		while (k >= t && ccw(ch[k - 2], ch[k - 1], ps[i]) <= 0) --k;
	ch.resize(k - 1);
	return ch;
}

VP ConvexCut(const VP &ps, L l) {
	VP Q;
	for (int i = 0; i < (int)ps.size(); i++) {
		P A = ps[i], B = ps[(i + 1) % ps.size()];
		if (ccw(l.a, l.b, A) != -1) Q.push_back(A);
		if (ccw(l.a, l.b, A) * ccw(l.a, l.b, B) < 0)
			Q.push_back(is_ll(L{ A, B }, l));
	}
	return Q;
}
double dist2(P a) {
	return real(a)*real(a) + imag(a)*imag(a);
}
// Suffix Array	?????????O(|S|log^2|S|), ????´¢O(|T|log|S|), ?????????????§????O(|S|)
class StringSearch {
	const int n;
	string S;
public:
	vector<int> sa, rank;
	StringSearch(const string &S_) :n(S_.size()), S(S_), sa(n + 1), rank(n + 1) {
		for (int i = 0; i <= n; i++) {
			sa[i] = i;
			rank[i] = i < n ? S[i] : -1;
		}
		vector<int> tmp(n + 1);
		for (int k = 1; k <= n; k *= 2) {
			auto Compare_SA = [=](int i, int j) {
				if (this->rank[i] != this->rank[j]) return this->rank[i] < this->rank[j];

				int ri = i + k <= n ? this->rank[i + k] : -1;
				int rj = j + k <= n ? this->rank[j + k] : -1;
				return ri < rj;
			};
			sort(sa.begin(), sa.end(), Compare_SA);
			tmp[sa[0]] = 0;
			for (int i = 1; i <= n; i++) {
				tmp[sa[i]] = tmp[sa[i - 1]] + (Compare_SA(sa[i - 1], sa[i]) ? 1 : 0);
			}
			for (int i = 0; i <= n; i++) {
				this->rank[i] = tmp[i];
			}
		}
	}
	bool Contain(const string &T) {
		int a = 0, b = n;
		while (b - a > 1) {
			int c = (a + b) / 2;
			if (S.compare(sa[c], T.length(), T) < 0) a = c;
			else b = c;
		}
		return S.compare(sa[b], T.length(), T) == 0;
	}
	vector<int> LCPArray() {
		for (int i = 0; i <= n; i++) rank[sa[i]] = i;
		int h = 0;
		vector<int> lcp(n + 1);
		for (int i = 0; i < n; i++) {
			int j = sa[rank[i] - 1];
			if (h > 0) h--;
			for (; j + h < n && i + h < n; h++) {
				if (S[j + h] != S[i + h]) break;
			}
			lcp[rank[i] - 1] = h;
		}
		return lcp;
	}
};
const double PI = acos(-1);

typedef complex<double> Comp;

bool is_pow_of2(int n) {
	int a = 1;
	while (n >= a) {
		if (n == a) {
			return true;
		}
		a <<= 1;
	}
	return false;
}


vector<Comp> fft(vector<Comp> v) {
	int n = v.size();
	//assert(is_pow_of2(n));

	for (int j = 1, i = 0; j < n - 1; j++) {
		for (int k = n >> 1; k >(i ^= k); k >>= 1);
		if (j < i) swap(v[i], v[j]);
	}

	for (int m = 2; m <= n; m <<= 1) {
		double deg = (-1) * 2 * PI / m;
		Comp r(cos(deg), sin(deg));
		for (int i = 0; i < n; i += m) {
			Comp w(1, 0);
			for (int j = i, k = i + m / 2; k < i + m; j++, k++) {
				Comp t1 = v[j], t2 = w*v[k];
				v[j] = t1 + t2, v[k] = t1 - t2;
				w *= r;
			}
		}
	}
	return v;
}

vector<Comp> ifft(vector<Comp> v) {
	int n = v.size();
	//assert(is_pow_of2(n));
	for (int j = 1, i = 0; j < n - 1; j++) {
		for (int k = n >> 1; k >(i ^= k); k >>= 1);
		if (j < i) swap(v[i], v[j]);
	}
	for (int m = 2; m <= n; m <<= 1) {
		double deg = 2 * PI / m;
		Comp r(cos(deg), sin(deg));
		for (int i = 0; i < n; i += m) {
			Comp w(1, 0);
			for (int j = i, k = i + m / 2; k < i + m; j++, k++) {
				Comp t1 = v[j], t2 = w*v[k];
				v[j] = t1 + t2, v[k] = t1 - t2;
				w *= r;
			}
		}
	}
	for (int i = 0; i < n; ++i) v[i] *= 1.0 / n;
	return v;
}

vector<Comp> convolution(vector<Comp> P, vector<Comp> Q) {
	P = fft(P), Q = fft(Q);
	for (int i = 0; i < P.size(); i++) P[i] *= Q[i];
	return ifft(P);
}
int main() {
	int n;
	cin >> n;
	vector<vi> c(n, vi(n));
	rep(i, n)rep(j, n)cin >> c[i][j];
	int size = 1;
	while (size < n*n)size *= 2;
	size *= 4;
	vector<Comp> a(size), b(size);
	vll ans(size);
	rep(i, n)rep(j, n)a[i * 2 * n + j] = b[(n - i) * 2 * n + (n - j)] = c[i][j];
	auto d=convolution(a, b);
	rep(i, size)ans[i] += d[i].real() + 0.1;
	vector<ll> a2(n*n * 2);
	ll num = 0;
	rep(i, n)rep(j, n) {
		if (i == 0 && j == 0)a2[i*i + j*j] += ans[(n + i) * 2 * n + (n + j)];
		else if (i == 0)a2[i*i + j*j] += ans[(n + i) * 2 * n + (n + j)] + ans[(n + i) * 2 * n + (n - j)];//+ ans[(n - i) * 2 * n + (n + j)] + ans[(n - i) * 2 * n + (n - j)];
		else if (j == 0)a2[i*i + j*j] += ans[(n + i) * 2 * n + (n + j)] + ans[(n - i) * 2 * n + (n + j)];// +ans[(n - i) * 2 * n + (n + j)] + ans[(n - i) * 2 * n + (n - j)];
		else a2[i*i + j*j] += ans[(n + i) * 2 * n + (n + j)] + ans[(n + i) * 2 * n + (n - j)] +ans[(n - i) * 2 * n + (n + j)] + ans[(n - i) * 2 * n + (n - j)];
	}
	double sum = 0;
	ll out = 0;
	ll d0n = 0;
	rep(i, n)rep(j, n)d0n += c[i][j] * (c[i][j] - 1) / 2;
	num += d0n * 2;
	rep1(i, n*n * 2-1) {
		sum += sqrt(i)*a2[i];
		num += a2[i];
	}
	cout << fixed << setprecision(10) << sum / num << endl;
	if (d0n) {
		out++;
		cout << "0 " << d0n << endl;
	}
	rep1(i,n*n*2-1) {
		if (a2[i] == 0)continue;
		cout << i << " " << a2[i]/2 << endl;
		out++;
		if (out == 10000)break;
	}

}