import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.StringTokenizer;
import java.util.TreeSet;

public class E2 {
	static PrintWriter out = new PrintWriter(System.out);
	static int M;
	static int maxN = 200001;
	static int curN;
	static Edge adj[][];
	static int curE[];
	static ArrayList<VecL[]> candidates;
	static int[] sz;
	static final boolean debug = false;
	public static void main(String[] args) {
		FS in = new FS();
		int N = in.nextInt();
		M = in.nextInt();

		ArrayDeque<Edge> es[] = new ArrayDeque[N];
		for(int i = 0; i < N; i++) es[i] = new ArrayDeque<Edge>();
		for(int i = 0; i < N-1; i++) {
			int u = in.nextInt()-1;
			int v = in.nextInt()-1;
			long m = in.nextLong();
			long b = in.nextLong();
			Edge e = new Edge(u,v,m,b);
			es[u].add(e);
			es[v].add(e);
		}

		curN = N;
		adj = new Edge[maxN][3];
		curE = new int[maxN];
		fixTree(0, -1, es);

		if(debug) {
			System.out.println("N= "+ curN);
			for(int i = 0; i < curN; i++) {
				System.out.println(i+" "+adj[i].toString());
			}
		}

		candidates = new ArrayList<>();
		sz = new int[curN];
		solve(0);

		CHT cht = new CHT(1);
		for(VecL[] cs : candidates) {
			for(VecL v : cs) {
				cht.add(v.x, v.y);
			}
		}

		for(int i = 0; i < M; i++) out.print(cht.query(i)+" ");
		out.println();
		out.close();
	}

	static void solve(int node) {
		szdfs(node, -1);
		if(sz[node] == 1) return; // base case, no edges
		Edge e = getEdgetroid(node, -1, sz[node], null);
		e.blocked = true;

		//solve on this guy
		ArrayList<VecL> left = new ArrayList<VecL>();
		ArrayList<VecL> right = new ArrayList<VecL>();
		makeLines(e.u, -1, e.m, e.b, left);
		makeLines(e.v, -1, 0, 0, right);

		VecL[] minskowski = minkowskiSum(getHull(removeDupes(left)), getHull(removeDupes(right)));
		candidates.add(minskowski);

		if(debug) {
			System.out.println("Decomp on: "+e);
			System.out.println("  Left: "+left.toString());
			System.out.println("  Right: "+right.toString());
			System.out.println("  Minskowski: "+Arrays.toString(minskowski));
		}

		//now solve on the remaining stuff
		solve(e.u);
		solve(e.v);
	}

	static void makeLines(int node, int p, long curM, long curB, ArrayList<VecL> vecs) {
		int to = 0;
		for(Edge e : adj[node]) {
			if(e == null || e.blocked || e.to(node) == p) continue;
			to++;
			makeLines(e.to(node), node, curM + e.m, curB + e.b, vecs);
		}
		if(to == 0) vecs.add(new VecL(curM, curB));
	}

	static Edge getEdgetroid(int node, int p, int totalSz, Edge curBest) {
		Edge myBest = null;
		for(Edge e : adj[node]) {
			if(e == null || e.blocked || e.to(node) == p) continue;
			if(myBest == null || myBest.getDif(totalSz) > e.getDif(totalSz)) {
				myBest = e;
			}
		}
		if(curBest != null && (myBest == null || curBest.getDif(totalSz) <= myBest.getDif(totalSz))) return curBest;
		return getEdgetroid(myBest.to(node), node, totalSz, myBest);
	}
	static void szdfs(int node, int p) {
		sz[node] = 1;
		for(Edge e : adj[node]) {
			if(e == null || e.blocked || e.to(node) == p) continue;
			szdfs(e.to(node), node);
			sz[node] += sz[e.to(node)];
		}
	}

	static void fixTree(int node, int p, ArrayDeque<Edge> es[]) {
		int cur = node;
		while(!es[node].isEmpty()) {
			if(es[node].size() <= 2) {
				while(!es[node].isEmpty()) {
					Edge e = es[node].poll();
					e.swap(node, cur);
					int to = e.to(cur);
					addEdge(cur, to, e, false, es);
					fixTree(to, cur, es);
				}
			}
			else {
				//add left edge to my current guy
				Edge e = es[node].poll();
				e.swap(node, cur);
				int to = e.to(cur);
				addEdge(cur, to, e, false, es);
				fixTree(to, cur, es);

				int next = curN++;
				Edge filler = new Edge(cur, next, 0, 0);
				addEdge(cur, next, filler, true, es);
				cur = next;
			}
		}
	}
	static void addEdge(int a, int b, Edge e, boolean filler, ArrayDeque<Edge> es[]) {
		adj[a][curE[a]++] = e;
		adj[b][curE[b]++] = e;
		if(!filler) es[b].remove(e);
	}

	static class Edge{
		int u,v;
		long m,b;
		boolean blocked;
		public Edge(int uu, int vv, long mm, long bb) {
			u=uu; v=vv; m=mm; b=bb;
			blocked = false;
		}
		int to(int node) { return u^v^node; }
		void swap(int from, int to) {
			if(u == from) u = to;
			else if(v == from) v = to;
			else throw null;
		}
		int getDif(int totalSz) {
			int s1 = Math.min(sz[u], sz[v]);
			int s2 = totalSz - Math.min(sz[u], sz[v]);
			return Math.abs(s1-s2);
		}
		@Override
		public String toString() {
			return "[u="+u+" v="+v+" m="+m+" b="+b+"]";
		}
	}

	// Computes the convex hull of the Minkowski sum of 2 convex polygons.
	// Polygons must be in CCW order and returns the sum in same order
	// O(N + M) where N and M are the sizes of the two polygons
	public static VecL[] minkowskiSum(VecL[] a, VecL[] b) {
		int n = a.length, m = b.length, k = 0;
		VecL[] h = new VecL[n+m];
		h[k++] = a[0].add(b[0]);
		for(int i = 0, j = 0; i+1 < n || j+1 < m;) {
			if(j+1 == m || (i+1 < n && (a[i+1].sub(a[i])).cross(b[j+1].sub(b[j])) < 0)){
				h[k++] = a[++i].add(b[j]);
			} else {
				h[k++] = a[i].add(b[++j]);
			}
		}
		if(k < 3) return Arrays.copyOf(h, k);
		VecL[] res = new VecL[n+m];
		int c = 0;
		for(int i = 0; i < k; i++) {
			if(valid(h[(i-1+k)%k], h[i], h[(i+1)%k])) res[c++] = h[i];
		}
		return Arrays.copyOf(res, c);
	}

	static boolean valid(VecL a, VecL b, VecL c) {
		VecL d1 = b.sub(a), d2 = c.sub(b);
		long cross = d1.cross(d2), dot = d1.dot(d2);
		return cross != 0 || (cross == 0 && dot < 0);
	}

	static VecL[] simple(VecL[] pnt, VecL[] c) {
		VecL[] res = new VecL[c.length];
		for(int i = 0; i < c.length; i++) res[i] = c[i].add(pnt[0]);
		return res;
	}

	static SegL[] toSeg(VecL pnts[]) {
		SegL[] res = new SegL[pnts.length];
		for(int i = 0; i < pnts.length; i++) {
			res[i] = new SegL(pnts[i], pnts[(i+1)%pnts.length]);
		}
		return res;
	}

	static boolean better(int i1, int i2, SegL c1[], SegL c2[]) {
		if(i1 == -1) return false;
		if(i2 == -1) return true;
		return c1[i1].dir.radialCompare(c2[i2].dir) <= 0;
	}

	static int getSt(SegL[] segs) {
		if(segs.length == 1) return -1;
		if(segs.length == 2) {
			if(segs[0].dir.y > 0 || (segs[0].dir.y == 0 && segs[0].dir.x > 0)) return 0;
			return 1;
		}
		for(int i = 0; i < segs.length; i++) {
			SegL prev = i == 0 ? segs[segs.length-1] : segs[i-1];
			SegL cur = segs[i];
			if(cur.dir.y >= 0 && prev.dir.y < 0) return i;
		}
		return -1;
	}

	public static VecL[] removeDupes(ArrayList<VecL> points) {
		HashSet<VecL> set=new HashSet<>();
		for (VecL v:points)
			set.add(v);
		int counter=0;
		VecL res[] = new VecL[set.size()];
		for (VecL v:set) res[counter++]=v;
		return res;
	}
	// returns the hull in CCW order
	public static VecL[] getHull(VecL[] points) {
		points=points.clone();
		Arrays.sort(points);
		if (points.length<3)
			return points;
		int n=points.length, j=2, k=2;
		VecL[] lo=new VecL[n], up=new VecL[n];
		lo[0]=points[0];
		lo[1]=points[1];
		for (int i=2; i<n; i++) {
			VecL p=points[i];
			while (j>1&&!right(lo[j-2], lo[j-1], p)) j--;
			lo[j++]=p;
		}
		up[0]=points[n-1];
		up[1]=points[n-2];
		for (int i=n-3; i>=0; i--) {
			VecL p=points[i];
			while (k>1&&!right(up[k-2], up[k-1], p)) k--;
			up[k++]=p;
		}

		VecL[] res=new VecL[j+k-2];
		for (int i=0; i<k; i++) res[i]=up[i];
		for (int i=1; i<j-1; i++) res[k+i-1]=lo[i];
		return res;
	}

	// check if a->b->c are in the right order
	static boolean right(VecL a, VecL b, VecL c) {
		return b.sub(a).cross(c.sub(a))>0;
	}

	static class VecL implements Comparable<VecL>{
		long x,y;
		public VecL(long xx, long yy) {
			x=xx;
			y=yy;
		}
		public VecL add(VecL o) { return new VecL(x+o.x, y+o.y);}
		public VecL sub(VecL o) { return new VecL(x-o.x, y-o.y);}
		public long cross(VecL o) { return x*o.y - y*o.x;}
		public long dot(VecL o) { return x*o.x + y*o.y;}
		public int quadrant() {
			if (x==0||y==0)
				if (y==0)
					if (x>=0)
						return 1;
					else
						return 3;
				else
					if (y>=0)
						return 2;
					else
						return 4;
			if (x>0)
				if (y>0)
					return 1;
				else
					return 4;
			else
				if (y>0)
					return 2;
				else
					return 3;
		}

		public int radialCompare(VecL o) {
			if (quadrant()==o.quadrant())
				return -Long.signum(cross(o));
			return Integer.compare(quadrant(), o.quadrant());
		}
		@Override
		public int compareTo(VecL o) {
			if(x!=o.x) return Long.compare(x, o.x);
			return Long.compare(y, o.y);
		}
		@Override
		public String toString() {
			return "("+x+","+y+")";
		}
		public int hashCode() {
			return Long.hashCode(x<<13^(y*57));
		}

		public boolean equals(Object oo) {
			VecL o=(VecL)oo;
			return x==o.x&&y==o.y;
		}
	}

	static class SegL{
		VecL from, to, dir;
		public SegL(VecL a, VecL b) {
			from=a;
			to=b;
			dir = to.sub(from);
		}
		@Override
		public String toString() {
			return "["+from+" -> "+to+"]";
		}
	}

	static class CHT {
		TreeSet<Line> hull;
		int type;  boolean query  = false;
		Comparator<Line> comp = new Comparator<Line>() {
			public int compare(Line a, Line b) {
				if (!query) return type * Long.compare(a.m, b.m);
				if (a.left == b.left) return Long.compare(a.m, b.m);
				return Double.compare(a.left, b.left);
			}
		};
		// -1 for min; +1 for max
		public CHT(int typee) { type = typee;  hull = new TreeSet<>(comp); }

		public void add(long m, long b) { add(new Line(m, b)); }

		public void add(Line a) {
			Line[] LR = { hull.lower(a), hull.ceiling(a) };
			for (int i = 0; i < 2; i++)
				if (LR[i] != null && LR[i].m == a.m) {
					if (type == 1 && LR[i].b >= a.b) return;
					if (type == -1 && LR[i].b <= a.b) return;
					remove(LR[i]);
				}
			hull.add(a);
			Line L = hull.lower(a), R = hull.higher(a);
			if (L != null && R != null && a.inter(R) <= R.left) {
				hull.remove(a);
				return;
			}
			Line LL = (L != null) ? hull.lower(L) : null;
			Line RR = (R != null) ? hull.higher(R) : null;
			if (L != null) a.left = a.inter(L);
			if (R != null) R.left = a.inter(R);
			while (LL != null && L.left >= a.inter(LL)) {
				remove(L);
				a.left = a.inter(L = LL);
				LL = hull.lower(L);
			}
			while (RR != null && R.inter(RR) <= a.inter(RR)) {
				remove(R);
				RR.left = a.inter(R = RR);
				RR = hull.higher(R);
			}
		}

		public long query(long x) {
			if(hull.size() == 0) return 0;
			Line temp = new Line(0, 0, 0);
			temp.left = x;  query = true;
			long ans = (long) hull.floor(temp).eval(x);
			query = false;  return ans;
		}

		private void remove(Line x) { hull.remove(x); }

		public int size() { return hull.size(); }

		static class Line {
			long m, b; double left = Long.MIN_VALUE;
			public Line(long mm, long x, long y) { m = mm; b = -m * x + y; }
			public Line(long mm, long bb) { m = mm; b = bb; }
			public long eval(long x) { return m * x + b; }
			public double inter(Line x) {
				return (double) (x.b - this.b) / (double) (this.m - x.m);
			}
		}
	}

	static class FS{
		BufferedReader br;
		StringTokenizer st;
		public FS() {
			br = new BufferedReader(new InputStreamReader(System.in));
		}
		String next() {
			while(st == null || !st.hasMoreElements()) {
				try {st = new StringTokenizer(br.readLine());}
				catch(Exception e) { throw null;}
			}
			return st.nextToken();
		}
		int nextInt() { return Integer.parseInt(next());}
		double nextDouble() { return Double.parseDouble(next());}
		long nextLong() { return Long.parseLong(next());}
		int[] NIA(int n) {
			int r[] = new int[n];
			for(int i = 0; i < n; i++) r[i] = nextInt();
			return r;
		}
		long[] NLA(int n) {
			long r[] = new long[n];
			for(int i = 0; i < n; i++) r[i] = nextLong();
			return r;
		}
		char[][] grid(int r, int c){
			char res[][] = new char[r][c];
			for(int i = 0; i < r; i++) {
				char l[] = next().toCharArray();
				for(int j = 0; j < c; j++) {
					res[i][j] = l[j];
				}
			}
			return res;
		}
	}

}


//package afc2020;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

public class G {
	Scanner in;
	PrintWriter out;
	String INPUT = "";

	void solve()
	{
		List<Integer> ins = new ArrayList<>();
		for(int i = 0;i < 11;i++) {
			ins.add(ni());
		}
		for(int i = ins.size()-1;i >= 0;i--) {
			double x = ins.get(i);
			double ans = x*x*x*5.0 + Math.sqrt(Math.abs(x));
			if(Math.abs(ans) >= 5000) {
				out.printf("f(%d) = MAGNA NIMIS!\n", ins.get(i), ans);
			}else {
				out.printf("f(%d) = %.2f\n", ins.get(i), ans);
			}
		}
	}

	void run() throws Exception
	{
		in = oj ? new Scanner(System.in) : new Scanner(INPUT);
		out = new PrintWriter(System.out);

		long s = System.currentTimeMillis();
		solve();
		out.flush();
		tr(System.currentTimeMillis()-s+"ms");
	}

	public static void main(String[] args) throws Exception
	{
		new G().run();
	}

	int ni() { return Integer.parseInt(in.next()); }
	long nl() { return Long.parseLong(in.next()); }
	double nd() { return Double.parseDouble(in.next()); }
	boolean oj = System.getProperty("ONLINE_JUDGE") != null;
	void tr(Object... o) { if(!oj)System.out.println(Arrays.deepToString(o)); }
}


/**
 * Created by IntelliJ IDEA.
 * User: Taras_Brzezinsky
 * Date: 8/24/11
 * Time: 8:54 PM
 * To change this template use File | Settings | File Templates.
 */

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.FileReader;
import java.util.StringTokenizer;
import java.io.IOException;
import java.util.Arrays;

public class TaskD extends Thread {

    public TaskD() {
        this.input = new BufferedReader(new InputStreamReader(System.in));
        this.output = new PrintWriter(System.out);
        this.setPriority(Thread.MAX_PRIORITY);
    }

    static class Matrix {
        long [][]grid;
        long [][]helper;
        public Matrix(int size, boolean E) {
            this.grid = new long[size][size];
            this.helper = new long [size][size];
            if (E) {
                for (int i = 0; i < grid.length; ++i) {
                    grid[i][i] = 1;
                }
            }
        }

        public Matrix pow(long power) {
            Matrix result = new Matrix(grid.length, true);
            Matrix stat = new Matrix(grid.length, false);
            for (int i = 0; i < grid.length; ++i) {
                stat.grid[i] = this.grid[i].clone();
            }
            for (; power != 0; power >>= 1) {
                if ((power & 1) == 1) {
                    result.multiply(stat);
                }
                stat.multiply(stat);
            }
            return result;
        }

        private void multiply(Matrix another) {
            assert(this.grid.length == another.grid.length);
            for (long[] current : helper) {
                Arrays.fill(current, 0);
            }
            for (int i = 0; i < grid.length; ++i) {
                for (int j = 0; j < grid.length; ++j) {
                    for (int k = 0; k < grid.length; ++k) {
                        helper[i][j] += (grid[i][k] * another.grid[k][j]) % MODULO;
                    }
                    helper[i][j] %= MODULO;
                }
            }
            for (int i = 0; i < grid.length; ++i) {
                System.arraycopy(helper[i], 0, grid[i], 0, grid.length);
            }
        }
    }

    static class Rule {
        char letter;
        char value;
        public Rule(char letter, char value) {
            this.letter = letter;
            this.value = value;
        }
    }

    private void generate(int index, int size) {
        if (index == initial.length) {
            states[size++] = initial.clone();
        } else {
            for (char value = 0; value < rules[index].value; ++value) {
                initial[index] = value;
                generate(index + 1, size);
            }
        }
    }

    private boolean isFinal(int index) {
        for (char letter = 'A'; letter <= 'Z'; ++letter) {
            boolean was = false, isGood = false;
            for (int i = 0; i < rules.length; ++i) {
                if (rules[i].letter == letter) {
                    was = true;
                    isGood |= states[index][i] == 0;
                }
            }
            if (was && !isGood) {
                return false;
            }
        }
        return true;
    }

    private void solve() throws Throwable {
        long n = nextLong();
        int c = nextInt();
        rules = new Rule[c];
        int total = 1;
        for (int i = 0; i < c; ++i) {
            rules[i] = new Rule(nextToken().charAt(0), (char)(nextInt()));
            total *= rules[i].value;
        }
        initial = new char [c];
        states = new char[total][];
        generate(0, 0);
        Matrix operator  = new Matrix(total, false);
        char []helper;
        for (int state = 0; state < total; ++state) {
            helper = states[state].clone();
            for (int i = 0; i < helper.length; ++i) {
                helper[i] = (char)((helper[i] + 1) % rules[i].value);
                for (int where = 0; where < total; ++where) {
                    boolean ok = true;
                    for (int index = 0; index < helper.length; ++index) {
                        ok &= helper[index] == states[where][index];
                    }
                    if (ok) {
                        operator.grid[state][where]++;
                        break;
                    }
                }
            }
        }
        Matrix finish = operator.pow(n);
        long result = 0;
        for (int current = 0; current < total; ++current) {
            if (isFinal(current)) {
                result = (result + finish.grid[0][current]) % MODULO;
            }
        }
        output.println(result);
    }

    public void run() {
        try {
            solve();
        } catch (Throwable e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
            System.exit(666);
        } finally {
            output.flush();
            output.close();
        }
    }

    public static void main(String[] args) {
        new TaskD().start();
    }

    private String nextToken() throws IOException {
        while (tokens == null || !tokens.hasMoreTokens()) {
            tokens = new StringTokenizer(input.readLine());
        }
        return tokens.nextToken();
    }

    private int nextInt() throws IOException {
        return Integer.parseInt(nextToken());
    }

    private double nextDouble() throws IOException {
        return Double.parseDouble(nextToken());
    }

    private long nextLong() throws IOException {
        return Long.parseLong(nextToken());
    }

    private static final int MODULO = 12345;
    private char [][]states;
    private char []initial;
    private Rule []rules;
    private BufferedReader input;
    private PrintWriter output;
    private StringTokenizer tokens = null;
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Iterator;
import java.io.BufferedWriter;
import java.util.InputMismatchException;
import java.io.IOException;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.util.NoSuchElementException;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author Rustam Musin (PloadyFree@gmail.com)
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        OutputWriter out = new OutputWriter(outputStream);
        TaskE solver = new TaskE();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskE {
        private int n;
        private boolean[] isRed;
        private int[] costRed;
        private int[] costBlue;

        public void solve(int testNumber, InputReader in, OutputWriter out) {
            input(in);
            out.print(solve());
        }

        private long solve() {
            int maxSaveRed = 120;
            int[][] dp = new int[1 << n][maxSaveRed];
            ArrayUtils.fill(dp, -1);
            dp[0][0] = 0;
            for (int curMask = 0; curMask < 1 << n; curMask++) {
                int haveRedCards = getCards(curMask, true);
                int haveBlueCards = getCards(curMask, false);
                for (int curSaveRed = 0; curSaveRed < maxSaveRed; curSaveRed++) {
                    if (dp[curMask][curSaveRed] == -1) {
                        continue;
                    }
                    int curSaveBlue = dp[curMask][curSaveRed];
                    for (int i = 0; i < n; i++) {
                        if (bit(curMask, i) == 0) {
                            int saveRed = Math.min(haveRedCards, costRed[i]);
                            int saveBlue = Math.min(haveBlueCards, costBlue[i]);
                            int nextSaveRed = curSaveRed + saveRed;
                            int nextMask = set(curMask, i);
                            if (dp[curMask][curSaveRed] + saveBlue > dp[nextMask][nextSaveRed]) {
                                dp[nextMask][nextSaveRed] = curSaveBlue + saveBlue;
                            }
                        }
                    }
                }
            }
            long totalRed = ArrayUtils.sumArray(costRed);
            long totalBlue = ArrayUtils.sumArray(costBlue);
            long ans = Math.max(totalRed, totalBlue);
            for (int i = 0; i < maxSaveRed; i++) {
                if (dp[(1 << n) - 1][i] == -1) {
                    continue;
                }
                ans = Math.min(ans, Math.max(totalRed - i, totalBlue - dp[(1 << n) - 1][i]));
            }
            return ans + n;
        }

        private void input(InputReader in) {
            n = in.readInt();
            isRed = new boolean[n];
            costRed = new int[n];
            costBlue = new int[n];
            for (int i = 0; i < n; i++) {
                isRed[i] = in.next().equals("R");
                costRed[i] = in.readInt();
                costBlue[i] = in.readInt();
            }
        }

        private int getCards(int m, boolean red) {
            int cnt = 0;
            for (int i = 0; i < n; i++) {
                if (bit(m, i) == 1 && isRed[i] == red) {
                    cnt++;
                }
            }
            return cnt;
        }

        private int set(int m, int b) {
            return m | (1 << b);
        }

        private int bit(int m, int b) {
            return (m >> b) & 1;
        }

    }

    static interface IntReversableCollection extends IntCollection {
    }

    static abstract class IntAbstractStream implements IntStream {

        public String toString() {
            StringBuilder builder = new StringBuilder();
            boolean first = true;
            for (IntIterator it = intIterator(); it.isValid(); it.advance()) {
                if (first) {
                    first = false;
                } else {
                    builder.append(' ');
                }
                builder.append(it.value());
            }
            return builder.toString();
        }

        public boolean equals(Object o) {
            if (!(o instanceof IntStream)) {
                return false;
            }
            IntStream c = (IntStream) o;
            IntIterator it = intIterator();
            IntIterator jt = c.intIterator();
            while (it.isValid() && jt.isValid()) {
                if (it.value() != jt.value()) {
                    return false;
                }
                it.advance();
                jt.advance();
            }
            return !it.isValid() && !jt.isValid();
        }

        public int hashCode() {
            int result = 0;
            for (IntIterator it = intIterator(); it.isValid(); it.advance()) {
                result *= 31;
                result += it.value();
            }
            return result;
        }

    }

    static class OutputWriter {
        private final PrintWriter writer;

        public OutputWriter(OutputStream outputStream) {
            writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
        }

        public OutputWriter(Writer writer) {
            this.writer = new PrintWriter(writer);
        }

        public void close() {
            writer.close();
        }

        public void print(long i) {
            writer.print(i);
        }

    }

    static interface IntIterator {
        public int value() throws NoSuchElementException;

        public boolean advance();

        public boolean isValid();

    }

    static interface IntList extends IntReversableCollection {
        public abstract int get(int index);

        public abstract void removeAt(int index);

        default public IntIterator intIterator() {
            return new IntIterator() {
                private int at;
                private boolean removed;

                public int value() {
                    if (removed) {
                        throw new IllegalStateException();
                    }
                    return get(at);
                }

                public boolean advance() {
                    at++;
                    removed = false;
                    return isValid();
                }

                public boolean isValid() {
                    return !removed && at < size();
                }

                public void remove() {
                    removeAt(at);
                    at--;
                    removed = true;
                }
            };
        }

    }

    static interface IntCollection extends IntStream {
        public int size();

    }

    static class IntArray extends IntAbstractStream implements IntList {
        private int[] data;

        public IntArray(int[] arr) {
            data = arr;
        }

        public int size() {
            return data.length;
        }

        public int get(int at) {
            return data[at];
        }

        public void removeAt(int index) {
            throw new UnsupportedOperationException();
        }

    }

    static class InputReader {
        private InputStream stream;
        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;
        private InputReader.SpaceCharFilter filter;

        public InputReader(InputStream stream) {
            this.stream = stream;
        }

        public int read() {
            if (numChars == -1) {
                throw new InputMismatchException();
            }
            if (curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = stream.read(buf);
                } catch (IOException e) {
                    throw new InputMismatchException();
                }
                if (numChars <= 0) {
                    return -1;
                }
            }
            return buf[curChar++];
        }

        public int readInt() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            int res = 0;
            do {
                if (c < '0' || c > '9') {
                    throw new InputMismatchException();
                }
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public String readString() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            StringBuilder res = new StringBuilder();
            do {
                if (Character.isValidCodePoint(c)) {
                    res.appendCodePoint(c);
                }
                c = read();
            } while (!isSpaceChar(c));
            return res.toString();
        }

        public boolean isSpaceChar(int c) {
            if (filter != null) {
                return filter.isSpaceChar(c);
            }
            return isWhitespace(c);
        }

        public static boolean isWhitespace(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

        public String next() {
            return readString();
        }

        public interface SpaceCharFilter {
            public boolean isSpaceChar(int ch);

        }

    }

    static interface IntStream extends Iterable<Integer>, Comparable<IntStream> {
        public IntIterator intIterator();

        default public Iterator<Integer> iterator() {
            return new Iterator<Integer>() {
                private IntIterator it = intIterator();

                public boolean hasNext() {
                    return it.isValid();
                }

                public Integer next() {
                    int result = it.value();
                    it.advance();
                    return result;
                }
            };
        }

        default public int compareTo(IntStream c) {
            IntIterator it = intIterator();
            IntIterator jt = c.intIterator();
            while (it.isValid() && jt.isValid()) {
                int i = it.value();
                int j = jt.value();
                if (i < j) {
                    return -1;
                } else if (i > j) {
                    return 1;
                }
                it.advance();
                jt.advance();
            }
            if (it.isValid()) {
                return 1;
            }
            if (jt.isValid()) {
                return -1;
            }
            return 0;
        }

        default public long sum() {
            long result = 0;
            for (IntIterator it = intIterator(); it.isValid(); it.advance()) {
                result += it.value();
            }
            return result;
        }

    }

    static class ArrayUtils {
        public static void fill(int[][] array, int value) {
            for (int[] row : array) {
                Arrays.fill(row, value);
            }
        }

        public static long sumArray(int[] array) {
            return new IntArray(array).sum();
        }

    }
}


/*Author LAVLESH*/
import java.util.*;
import java.io.*;
public class solution{
	static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
	static StringTokenizer st=new StringTokenizer("");

	static public String next() {
	        while (st == null || !st.hasMoreTokens()) {
	            try {
	                st = new StringTokenizer(br.readLine());
	            } catch (IOException e) {
	                throw new RuntimeException(e);
	            }
	        }
	        return st.nextToken();
	    }
	static long[]rc =new long[1000001];
	static int mod=1000003;
	static long inv(long a,int m){
		if(a==1)return 1;
		return inv(m%a,m)*(m-m/a)%m;
	}
	public static void main(String[]args)throws IOException{
		PrintWriter op =new PrintWriter(System.out,true);
		 int n=Integer.parseInt(next());
		 int m=Integer.parseInt(next());
		rc[m]=1;
		 for(int i=m+1;i<=n+m;i++)
			rc[i]=rc[i-1]*i%mod*inv(i-m,mod)%mod;
		op.println(rc[n+m]-1);

		 op.close();
  }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.InputMismatchException;
import java.io.IOException;
import java.io.InputStream;

/**
 * Built using CHelper plug-in Actual solution is at the top
 *
 * @author svilen.marchev@gmail.com
 */
public class Main {
  public static void main(String[] args) {
    InputStream inputStream = System.in;
    OutputStream outputStream = System.out;
    InputReader in = new InputReader(inputStream);
    PrintWriter out = new PrintWriter(outputStream);
    TaskF solver = new TaskF();
    solver.solve(1, in, out);
    out.close();
  }

  static class TaskF {
    public void solve(int testNumber, InputReader in, PrintWriter out) {
      String s = in.readString();
//    String vow = "AEIOUY";

//    "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
      String straightLetters = "ABEFGHIJKMNOPQRSTUVWXZ";

      boolean allStraight = true;
      for (char c : s.toCharArray()) {
        allStraight &= straightLetters.contains("" + c);
      }
      out.println(allStraight ? "YES" : "NO");

//    int numVows = 0;
//    Set<Character> cons = new HashSet<>();
//    for (char c : s.toCharArray()) {
//      if (vow.contains(""+c)) {
//        numVows++;
//      } else {
//        cons.add(c);
//      }
//    }
//    out.println(numVows < cons.size() ? "YES" : "NO");
    }

  }

  static class InputReader {
    private InputStream stream;
    private byte[] buf = new byte[1024];
    private int curChar;
    private int numChars;
    private InputReader.SpaceCharFilter filter;

    public InputReader(InputStream stream) {
      this.stream = stream;
    }

    public int read() {
      if (numChars == -1) {
        throw new InputMismatchException();
      }
      if (curChar >= numChars) {
        curChar = 0;
        try {
          numChars = stream.read(buf);
        } catch (IOException e) {
          throw new InputMismatchException();
        }
        if (numChars <= 0) {
          return -1;
        }
      }
      return buf[curChar++];
    }

    public String readString() {
      int c = read();
      while (isSpaceChar(c)) {
        c = read();
      }
      StringBuilder res = new StringBuilder();
      do {
        if (Character.isValidCodePoint(c)) {
          res.appendCodePoint(c);
        }
        c = read();
      } while (!isSpaceChar(c));
      return res.toString();
    }

    public boolean isSpaceChar(int c) {
      if (filter != null) {
        return filter.isSpaceChar(c);
      }
      return isWhitespace(c);
    }

    public static boolean isWhitespace(int c) {
      return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
    }

    public interface SpaceCharFilter {
      public boolean isSpaceChar(int ch);

    }

  }
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
/*
3 3 5
0 1 1 0 7
1 1 1 0 10
1 1 1 1 2
1 1 1 2 2
0 1 1 2 3

500 500 2
0 1 1 0 4
0 1 1 1 2

500 500 8
100 100 5 5 1
100 101 5 5 2
101 100 5 5 3
101 101 5 5 4
100 100 6 5 5
100 101 6 5 6
101 100 6 5 7
101 101 6 5 8

6 7 6
0 2 2 2 100
0 3 3 3 100
0 2 2 1 201
0 3 3 2 5
1 2 1 3 1000
1 2 1 2 1000
200

6 7 6
0 2 2 2 100
0 3 3 3 100
0 2 2 1 194
0 3 3 2 5
1 2 1 3 1000
1 2 1 2 1000
199

6 7 6
1 2 2 3 100
1 3 3 4 100
1 2 2 2 194
1 3 3 3 5
2 2 1 4 1000
2 2 1 3 1000
0
 */
public class C {

	static long[][] costOf;
	static BonusAtSquare[][] hBonuses, vBonuses;
	static int maxX, maxY;
	static long[][][][] dp;

	public static void main(String[] args) {
		FastScanner fs=new FastScanner();
		maxX=fs.nextInt();
		maxY=fs.nextInt();
		int nRobots=fs.nextInt();
		costOf=new long[maxX][maxY];
		hBonuses=new BonusAtSquare[maxX][maxY];
		vBonuses=new BonusAtSquare[maxX][maxY];
		for (int x=0; x<maxX; x++)
			for (int y=0; y<maxY; y++) {
				hBonuses[x][y]=new BonusAtSquare();
				vBonuses[x][y]=new BonusAtSquare();
			}

		for (int i=0; i<nRobots; i++) {
			int x=fs.nextInt(), y=fs.nextInt(), d=fs.nextInt(), t=fs.nextInt();
			long killCost=fs.nextInt();
			processTransformer(x, y, d, t, killCost);
		}
		long startTime=System.currentTimeMillis();
		dp=new long[2][maxY+1][511][2];
		for (int i=0; i<dp.length; i++)
			for (int j=0; j<dp[i].length; j++)
				for (int k=0; k<dp[i][j].length; k++)
					Arrays.fill(dp[i][j][k], -1);
		for (int xAt=maxX-1; xAt>=0; xAt--) {
			for (int j=0; j<dp[0].length; j++)
				for (int k=0; k<dp[0][j].length; k++)
					Arrays.fill(dp[(xAt&1)][j][k], -1);

			for (int yAt=maxY-1; yAt>=0; yAt--) {
				for (int goingH=0; goingH<2; goingH++) {
					for (int timesGoingStraight=0; timesGoingStraight<=(goingH==0?xAt:yAt); timesGoingStraight++) {
						go(xAt, yAt, timesGoingStraight, goingH);
					}
				}
			}
		}
		long ans=go(0, 0, 0, 0);
		System.out.println(ans);
		System.err.println(System.currentTimeMillis()-startTime);
	}

	static long go(int x, int y, int timesGoingStraight, int goingH) {
		if (x==maxX-1&&y==maxY-1) {
			return 0;
		}
		if (dp[(x&1)][y][timesGoingStraight][goingH]!=-1) return dp[(x&1)][y][timesGoingStraight][goingH];
		long curCost=costOf[x][y];
		if (goingH==1)
			curCost-=hBonuses[x][y].getBonus(timesGoingStraight);
		else
			curCost-=vBonuses[x][y].getBonus(timesGoingStraight);
//		System.out.println("Cur cost "+x+" "+y+" is "+curCost);
		long best;
		if (goingH==1) {
			long goH=y+1<maxY?go(x, y+1, timesGoingStraight+1, 1):Long.MAX_VALUE;
			long goV=x+1<maxX?go(x+1, y, 1, 0):Long.MAX_VALUE;
			best=Math.min(goH, goV)+curCost;
		}
		else {
			long goH=y+1<maxY?go(x, y+1, 1, 1):Long.MAX_VALUE;
			long goV=x+1<maxX?go(x+1, y, timesGoingStraight+1, 0):Long.MAX_VALUE;
			best=Math.min(goH, goV)+curCost;
		}
//		System.out.println(x+" "+y+" "+timesGoingStraight+" "+goingH);
		return dp[(x&1)][y][timesGoingStraight][goingH]=best;
	}

	//have to process these in increasing straight distance
	static class BonusAtSquare {
		PriorityQueue<Bonus> pq=new PriorityQueue<>();
		public long bonusAdded=0;

		public void addBonus(int goingStraightFor, long bonus) {
			pq.add(new Bonus(goingStraightFor, bonus));
		}

		public long getBonus(int goingStraightFor) {
			while (!pq.isEmpty()&&goingStraightFor>=pq.peek().goingStraightFor) {
				Bonus next=pq.remove();
				bonusAdded+=next.points;
			}
			return bonusAdded;
		}
	}

	static class Bonus implements Comparable<Bonus> {
		int goingStraightFor;
		long points;
		public Bonus(int goingStraightFor, long points) {
			this.goingStraightFor=goingStraightFor;
			this.points=points;
		}

		public int compareTo(Bonus o) {
			return Integer.compare(goingStraightFor, o.goingStraightFor);
		}
	}

	static void processTransformer(int x, int y, int d, int t, long fightCost) {
//		System.out.println("Processing transformer ("+x+", "+y+") d: "+d+" startTime: "+t+" fightCost: "+fightCost);
		int[] xs=new int[4];
		int[] ys=new int[4];
		int[] firstTime=new int[4];
		xs[0]=x;
		ys[0]=y;
		firstTime[0]=t;
		xs[1]=x+d;
		ys[1]=y-d;
		firstTime[1]=t+1;
		xs[2]=x+d;
		ys[2]=y;
		firstTime[2]=t+2;
		xs[3]=x;
		ys[3]=y+d;
		firstTime[3]=t+3;

		ArrayList<Integer> xMatters=new ArrayList<>();
		ArrayList<Integer> yMatters=new ArrayList<>();
		for (int i=0; i<4; i++) {
			if (matters(xs[i], ys[i], firstTime[i])) {
				xMatters.add(xs[i]);
				yMatters.add(ys[i]);
			}
		}
//		System.out.println("\t"+xMatters+" "+yMatters);
		if (xMatters.size()>2) throw null;
		if (xMatters.size()==0) return;
		if (xMatters.size()==1) {
			costOf[xMatters.get(0)][yMatters.get(0)]+=fightCost;
		}
		else {
			costOf[xMatters.get(0)][yMatters.get(0)]+=fightCost;
			costOf[xMatters.get(1)][yMatters.get(1)]+=fightCost;
			if (xMatters.get(0)==(int)xMatters.get(1)) {
				hBonuses[xMatters.get(1)][yMatters.get(1)].addBonus(d, fightCost);
			}
			else {
				if (yMatters.get(0)==(int)yMatters.get(1)) {
					vBonuses[xMatters.get(1)][yMatters.get(1)].addBonus(d, fightCost);
				}
			}
		}
	}

	static boolean matters(int x, int y, int t) {
		if (x+y<t) return false;
		if ((t-x-y)%4!=0) return false;
		return true;
	}

	static class FastScanner {
		BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st=new StringTokenizer("");
		public String next() {
			while (!st.hasMoreElements())
				try {
					st=new StringTokenizer(br.readLine());
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			return st.nextToken();
		}

		public int nextInt() {
			return Integer.parseInt(next());
		}
	}

}


import java.io.*;
import java.math.*;
import java.util.*;

public class C_hackIt {
    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in);
        PrintWriter pw = new PrintWriter(System.out);

        BigInteger mod = sc.nextBigInteger();
        BigInteger l = new BigInteger("1");
        BigInteger r = new BigInteger("45");
        BigInteger ten = new BigInteger("10");
        for (int i = 0; i < 20; i++) {
        	r = r.multiply(new BigInteger("10")).add(ten.multiply(new BigInteger("45")));
        	ten = ten.multiply(new BigInteger("10"));
        }
        r = r.add(l);
        BigInteger rem = mod.subtract(r.mod(mod));
        BigInteger end = new BigInteger("1000000000000000000000");
        l = l.add(rem);
        end = end.add(rem);
        pw.println(l + " " + end);

        pw.close();
    }
    static class FastScanner {
    	private boolean finished = false;

        private InputStream stream;
        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;
        private SpaceCharFilter filter;

        public FastScanner(InputStream stream) {
            this.stream = stream;
        }

        public int read() {
            if (numChars == -1) {
                throw   new InputMismatchException();
            }
            if (curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = stream.read(buf);
                } catch (IOException e) {
                    throw new InputMismatchException();
                }
                if (numChars <= 0) {
                    return -1;
                }
            }
            return buf[curChar++];
        }
        public int peek() {
            if (numChars == -1) {
                return -1;
            }
            if (curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = stream.read(buf);
                } catch (IOException e) {
                    return -1;
                }
                if (numChars <= 0) {
                    return -1;
                }
            }
            return buf[curChar];
        }

        public int nextInt() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            int res = 0;
            do {
                if (c < '0' || c > '9') {
                    throw new InputMismatchException();
                }
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }
        public long nextLong() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            long res = 0;
            do {
                if (c < '0' || c > '9') {
                    throw new InputMismatchException();
                }
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public String nextString() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            StringBuilder res = new StringBuilder();
            do {
                if (Character.isValidCodePoint(c)) {
                    res.appendCodePoint(c);
                }
                c = read();
            } while (!isSpaceChar(c));
            return res.toString();
        }

        public boolean isSpaceChar(int c) {
            if (filter != null) {
                return filter.isSpaceChar(c);
            }
            return isWhitespace(c);
        }

        public static boolean isWhitespace(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

        private String readLine0() {
            StringBuilder buf = new StringBuilder();
            int c = read();
            while (c != '\n' && c != -1) {
                if (c != '\r') {
                    buf.appendCodePoint(c);
                }
                c = read();
            }
            return buf.toString();
        }
        public String readLine() {
            String s = readLine0();
            while (s.trim().length() == 0) {
                s = readLine0();
            }
            return s;
        }
        public String readLine(boolean ignoreEmptyLines) {
            if (ignoreEmptyLines) {
                return readLine();
            } else {
                return readLine0();
            }
        }

        public BigInteger readBigInteger() {
            try {
                return new BigInteger(nextString());
            } catch (NumberFormatException e) {
                throw new InputMismatchException();
            }
        }

        public char nextCharacter() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            return (char) c;
        }

        public double nextDouble() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            double res = 0;
            while (!isSpaceChar(c) && c != '.') {
                if (c == 'e' || c == 'E') {
                    return res * Math.pow(10, nextInt());
                }
                if (c < '0' || c > '9') {
                    throw new InputMismatchException();
                }
                res *= 10;
                res += c - '0';
                c = read();
            }
            if (c == '.') {
                c = read();
                double m = 1;
                while (!isSpaceChar(c)) {
                    if (c == 'e' || c == 'E') {
                        return res * Math.pow(10, nextInt());
                    }
                    if (c < '0' || c > '9') {
                        throw new InputMismatchException();
                    }
                    m /= 10;
                    res += (c - '0') * m;
                    c = read();
                }
            }
            return res * sgn;
        }

        public boolean isExhausted() {
            int value;
            while (isSpaceChar(value = peek()) && value != -1) {
                read();
            }
            return value == -1;
        }
    public String next() {
            return nextString();
        }

        public SpaceCharFilter getFilter() {
            return filter;
        }

        public void setFilter(SpaceCharFilter filter) {
            this.filter = filter;
        }

        public interface SpaceCharFilter {
            public boolean isSpaceChar(int ch);
        }
        public int[] nextIntArray(int n){
            int[] array=new int[n];
            for(int i=0;i<n;++i)array[i]=nextInt();
            return array;
        }
        public int[] nextSortedIntArray(int n){
            int array[]=nextIntArray(n);
            PriorityQueue<Integer> pq = new PriorityQueue<Integer>();
            for(int i = 0; i < n; i++){
                pq.add(array[i]);
            }
            int[] out = new int[n];
            for(int i = 0; i < n; i++){
                out[i] = pq.poll();
            }
            return out;
        }
        public int[] nextSumIntArray(int n){
            int[] array=new int[n];
            array[0]=nextInt();
            for(int i=1;i<n;++i)array[i]=array[i-1]+nextInt();
            return array;
        }
        public ArrayList<Integer>[] nextGraph(int n, int m){
            ArrayList<Integer>[] adj = new ArrayList[n];
            for(int i = 0; i < n; i++){
                adj[i] = new ArrayList<Integer>();
            }
            for(int i = 0; i < m; i++){
                int u = nextInt(); int v = nextInt();
                u--; v--;
                adj[u].add(v); adj[v].add(u);
            }
            return adj;
        }
        public ArrayList<Integer>[] nextTree(int n){
            return nextGraph(n, n-1);
        }

        public long[] nextLongArray(int n){
            long[] array=new long[n];
            for(int i=0;i<n;++i)array[i]=nextLong();
            return array;
        }
        public long[] nextSumLongArray(int n){
            long[] array=new long[n];
            array[0]=nextInt();
            for(int i=1;i<n;++i)array[i]=array[i-1]+nextInt();
            return array;
        }
        public long[] nextSortedLongArray(int n){
            long array[]=nextLongArray(n);
            Arrays.sort(array);
            return array;
        }
    }
	static void shuffle(int[] a) {
		Random get = new Random();
		for (int i = 0; i < a.length; i++) {
			int r = get.nextInt(a.length);
			int temp = a[i];
			a[i] = a[r];
			a[r] = temp;
		}
	}
	static void shuffle(long[] a) {
		Random get = new Random();
		for (int i = 0; i < a.length; i++) {
			int r = get.nextInt(a.length);
			long temp = a[i];
			a[i] = a[r];
			a[r] = temp;
		}
	}
}


import java.io.*;
import java.math.BigInteger;
import java.util.*;

public class E {

    void solve() throws IOException {
        char[] c = reader.readLine().toCharArray();
        for (int i = 1; i <= 10000; i++) {
            int[] a = doit(i, c);
            if (a != null) {
                out.println(i);
                for (int j = 0; j < a.length; j++) {
                    out.print(a[j] + " ");
                }
                break;
            }
        }
    }

    int[] doit(int n, char[] c) {
        index = 0;
        int[] a = new int[n];
        int[] b = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = b[i] = i + 1;
        }
        boolean res = simulateMerge(0, n, c, a, b);
        if (res && index == c.length) {
            for (int i = 0; i < n; i++) {
                b[a[i] - 1] = i + 1;
            }
            return b;
        } else {
            return null;
        }
    }

    int index = 0;

    boolean simulateMerge(int l, int r, char[] c, int[] a, int[] b) {
        if (r - l <= 1) return true;
        int m = (l + r) >> 1;
        simulateMerge(l, m, c, a, b);
        simulateMerge(m, r, c, a, b);
        int i = l;
        int j = m;
        int k = l;
        while (i < m && j < r) {
            if (index == c.length) {
                return false;
            }
            if (c[index] == '0') {
                b[k] = a[i];
                i++;
            } else {
                b[k] = a[j];
                j++;
            }
            index++;
            k++;
        }

        while (i < m) {
            b[k] = a[i];
            i++;
            k++;
        }
        while (j < r) {
            b[k] = a[j];
            j++;
            k++;
        }
        for (int x = l; x < r; x++) {
            a[x] = b[x];
        }
        return true;
    }

    public static void main(String[] args) throws IOException {
        new E().run();
    }

    void run() throws IOException {
        reader = new BufferedReader(new InputStreamReader(System.in));
//		reader = new BufferedReader(new FileReader("input.txt"));
        tokenizer = null;
        out = new PrintWriter(new OutputStreamWriter(System.out));
//		out = new PrintWriter(new FileWriter("output.txt"));
        solve();
        reader.close();
        out.flush();

    }

    BufferedReader reader;
    StringTokenizer tokenizer;
    PrintWriter out;

    int nextInt() throws IOException {
        return Integer.parseInt(nextToken());
    }

    long nextLong() throws IOException {
        return Long.parseLong(nextToken());
    }

    double nextDouble() throws IOException {
        return Double.parseDouble(nextToken());
    }

    int[] nextIntArray(int n) throws IOException {
        int[] result = new int[n];
        for (int i = 0; i < n; i++) {
            result[i] = nextInt();
        }
        return result;
    }

    long[] nextLongArray(int n) throws IOException {
        long[] result = new long[n];
        for (int i = 0; i < n; i++) {
            result[i] = nextLong();
        }
        return result;
    }

    double[] nextDoubleArray(int n) throws IOException {
        double[] result = new double[n];
        for (int i = 0; i < n; i++) {
            result[i] = nextDouble();
        }
        return result;
    }

    int[][] nextIntArray(int n, int m) throws IOException {
        int[][] result = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                result[i][j] = nextInt();
            }
        }
        return result;
    }

    long[][] nextLongArray(int n, int m) throws IOException {
        long[][] result = new long[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                result[i][j] = nextLong();
            }
        }
        return result;
    }

    double[][] nextDoubleArray(int n, int m) throws IOException {
        double[][] result = new double[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                result[i][j] = nextDouble();
            }
        }
        return result;
    }

    String nextToken() throws IOException {
        while (tokenizer == null || !tokenizer.hasMoreTokens()) {
            tokenizer = new StringTokenizer(reader.readLine());
        }
        return tokenizer.nextToken();
    }
}


import java.util.*;
import java.math.*;
import java.io.*;

// I hate this problem
public class CF1062F {
	class Graph {
		int n, cntfwd[], cntrev[], adj[][], rev[][];
		public Graph(int nn, int[][] edge) {
			n = nn;
			adj = new int[n][];
			rev = new int[n][];
			cntfwd = new int[n];
			cntrev = new int[n];
			for(int i = 0 ; i < edge[0].length ; i++) {
				cntfwd[edge[0][i]]++;
				cntrev[edge[1][i]]++;
			}
			for(int i = 0 ; i < n ; i++) {
				adj[i] = new int[cntfwd[i]];
				rev[i] = new int[cntrev[i]];
			}
			for(int i = 0 ; i < edge[0].length ; i++)
				add(edge[0][i], edge[1][i]);
		}
		void add(int u, int v) {
			adj[u][--cntfwd[u]] = v;
			rev[v][--cntrev[v]] = u;
		}
		int solve() {

			int res = 0;

			/*

			All important nodes must lie on the longest path.

			Let u be an important node.
			Let path P = p[1] -> p[2] -> p[3] -> ... -> p[k]
			Assume P is the longest path and u is not on P.
			One of three cases will happen with u and P:

				1. u has a path to all p[i]. Because of this, the path Q can be constructed.
				   Q = u -> p[1] -> p[2] -> p[3] -> ... -> p[k].
				   Q is a longer path than P.

				2. All p[i] has a path to u. Because of this, the path Q can be constructed.
				   Q = p[1] -> p[2] -> p[3] -> ... -> p[k] -> u.
				   Q is a longer path than P.

				3. There is some p[i] that u does not have a path to and p[i + 1] that u has a path to.
				   P can be partitioned into two pieces, p[1, i] and p[i + 1, k]. The path Q can be
				   constructed from these partitions, where u can be placed between them.
				   Q = p[1] -> p[2] -> p[3] -> ... -> p[i] -> u -> p[i + 1] -> ... -> p[k]
				   Q is a longer path than P.

			P is not the longest path in all cases. If there is a path that does not contain an important node,
			a longer path can always be constructed using the important node.

			Therefore, all important nodes must lie on the longest path.

			 */
			dp = new int[n];
			path = new int[n];
			Arrays.fill(dp, -1);
			Arrays.fill(path, -1);
			for(int i = 0 ; i < n ; i++)
				go(i);
			int maxpath = 0;
			int maxpathnode = -1;
			for(int i = 0 ; i < n ; i++) {
				if(dp[i] > maxpath) {
					maxpath = dp[i];
					maxpathnode = i;
				}
			}

			int[] p = new int[dp[maxpathnode] + 1];
			{
				int ind = 0;
				int u = maxpathnode;
				while(u != -1) {
					p[ind++] = u;
					u = path[u];
				}
			}

			/*

			To count all important nodes, we need to find the in[u] and out[u] for all nodes
			along P. in[u] will be the number of nodes that have a path to u, and out[u] will
			be the number of nodes u has a path to.

			for each i in P[1 : k]:
				Let u = p[i].

				BFS from u using the reverse edges of the graph, all nodes that u can reach using
				the reverse edges means that all those nodes can reach u using the normal edges.
				Only visit each node once, and keep count of the reachable nodes. Don't visit
				nodes on P.

				in[u] += reachableNodes.

				After that, add in the nodes from in[p[i - 1]] if u is not the first node in the path.
				Add an extra 1 to include the previous node as it reaches u.

				if i > 0: in[u] += in[p[i - 1]] + 1

			for each i in P[k : 1]:
				Let u = p[i].

				BFS from u using the normal edges of the graph. Only visit each node once, and keep
				count of the reachable nodes. Don't visit nodes on P.

				out[u] += reachableNodes.

				After that, add in the nodes of out[p[i + 1]] if u is not the last node in the path.
				Add an extra 1 to include the next node as reachable.

				if i < len(p): out[u] += out[p[i + 1]] + 1

			for each i in P[1 : k]
				Let u = p[i].

				If in[u] + out[u] is equal to n - 1, then u is an important node.
				If in[u] + out[u] is equal to n - 2, then u is a semi-important node.

			 */

			int[] in = new int[n];
			int[] out = new int[n];

			Deque<Integer> q = new ArrayDeque<>();
			boolean[] visited = new boolean[n];
			for(int i = 0 ; i < p.length ; i++)
				visited[p[i]] = true;
			for(int i = 0 ; i < p.length ; i++) {
				int reachableNodes = 0;
				q.add(p[i]);
				while(!q.isEmpty()) {
					int u = q.pop();
					for(int v : rev[u]) {
						if(!visited[v]) {
							visited[v] = true;
							q.add(v);
							reachableNodes++;
						}
					}
				}
				in[p[i]] += reachableNodes;
				if(i > 0) in[p[i]] += in[p[i - 1]] + 1;
			}
			Arrays.fill(visited, false);
			for(int i = 0 ; i < p.length ; i++)
				visited[p[i]] = true;
			for(int i = p.length - 1 ; i >= 0 ; i--) {
				int reachableNodes = 0;
				q.add(p[i]);
				while(!q.isEmpty()) {
					int u = q.pop();
					for(int v : adj[u]) {
						if(!visited[v]) {
							visited[v] = true;
							q.add(v);
							reachableNodes++;
						}
					}
				}
				out[p[i]] += reachableNodes;
				if(i < p.length - 1) out[p[i]] += out[p[i + 1]] + 1;
			}

			for(int i = 0 ; i < p.length ; i++)
				if(in[p[i]] + out[p[i]] >= n - 2)
					res++;

			/*

			All nodes along P are done, now the rest of the nodes must be dealt with.

			For all nodes u that are not in P, we need to determine whether they
			are semi-important or not. We will need to find candidates that might
			possibly be semi-important
			Let L[u] = p[i] = rightmost node from the left that can reach u
			Let R[u] = p[j] = leftmost node from the right that u can reach

			If for any u, j - i - 1 > 1, then u is not semi-important.
			Side-note* if j - i - 1 is 0, then u is on the longest path, so for u
			           to be semi-important, j - i - 1 must only be 1

			p[i] -> j - i - 1 nodes -> p[j]
			 |                          ^
			 |-----------> u -----------|

			 All the nodes on p that are between p[i] and p[j] are not reachable from u,
			 and u cannot reach any of them, so if there is more than 1 node along that path,
			 u cannot be semi-important.

			 If u does not fail this, add it to the candidates list.

			 If there are 2 candidate nodes, u and v, where L[v] = L[u]
			 (which also means R[v] = R[u]), then both u and v cannot be
			 candidates. Here are the cases:

			 Let d be the node in between p[i] and p[j].

			 p[i] -> d -> p[j]
			 | |           ^ ^
			 | |---> u ----| |
			 |               |
			 |-----> v ------|

			 In this case, d cannot be reached by u and u cannot be reached by d. This same
			 statement can be applied to v if d is replaced by v. Meaning there are at least
			 2 nodes that are both unreachable and do not reach u. Same goes for v.
			 u and v can be removed from the candidates list.

			 p[i] -> d -> p[j]
			  |            ^
			  |-> v --> u -|

			 p[i] -> d -> p[j]
			  |            ^
			  |-> u --> v -|

			 In these cases, u and v combined with P create a longer path than the already
			 established longest path, so these cases are impossible.

			 */

			int[] L = new int[n];
			int[] R = new int[n];
			Arrays.fill(L, -1);
			Arrays.fill(R, -1);
			Arrays.fill(visited, false);
			for(int i = 0 ; i < p.length ; i++)
				visited[p[i]] = true;
			for(int i = p.length - 1 ; i >= 0 ; i--) {
				q.add(p[i]);
				while(!q.isEmpty()) {
					int u = q.pop();
					for(int v : adj[u]) {
						if(!visited[v]) {
							visited[v] = true;
							L[v] = i;
							q.add(v);
						}
					}
				}
			}
			Arrays.fill(visited, false);
			for(int i = 0 ; i < p.length ; i++)
				visited[p[i]] = true;
			for(int i = 0 ; i < p.length ; i++) {
				q.add(p[i]);
				while(!q.isEmpty()) {
					int u = q.pop();
					for(int v : rev[u]) {
						if(!visited[v]) {
							visited[v] = true;
							R[v] = i;
							q.add(v);
						}
					}
				}
			}
			int[] candidate = new int[p.length];
			Arrays.fill(candidate, -1);
			boolean[] valid = new boolean[p.length];
			Arrays.fill(valid, true);
			for(int i = 0 ; i < n ; i++) {
				if(L[i] != -1 && R[i] != -1) {
					if(R[i] - L[i] - 1 == 1) {
						if(candidate[L[i]] != -1) {
							valid[L[i]] = false;
						} else {
							candidate[L[i]] = i;
						}
					}
				}
			}
			List<Integer> specialCases = new ArrayList<>();
			for(int i = 0 ; i < n ; i++)
				if((L[i] == -1 && 0 <= R[i] && R[i] <= 1) || (L[i] > p.length - 2 && R[i] == -1))
					specialCases.add(i);
			for(int i = 0 ; i < p.length ; i++)
				if(valid[i] && candidate[i] == -1)
					valid[i] = false;

			/*

			All candidate nodes have some p[i] -> candidate[c] -> p[i + 2]. Because of this, the
			same process can be used to calculate the in[candidate[c]] and out[candidate[c]] that
			was used on all nodes in P. It's just that instead of starting the BFS at some p[i], it
			starts at candidate[c].

			*/
			Arrays.fill(visited, false);
			for(int i = 0 ; i < p.length ; i++)
				visited[i] = true;
			for(int i = 0 ; i < p.length ; i++) {
				if(valid[i]) {
					int reachableNodes = 0;
					q.add(candidate[i]);
					visited[candidate[i]] = true;
					while(!q.isEmpty()) {
						int u = q.pop();
						for(int v : rev[u]) {
							if(!visited[v]) {
								visited[v] = true;
								q.add(v);
								reachableNodes++;
							}
						}
					}
					visited[candidate[i]] = false;
					in[candidate[i]] += reachableNodes;
					if(i > 0) in[candidate[i]] += in[p[i - 1]] + 1;
				}
			}

			Arrays.fill(visited, false);
			for(int i = 0 ; i < p.length ; i++)
				visited[p[i]] = true;
			for(int i = p.length - 1 ; i >= 0 ; i--) {
				if(valid[i]) {
					int reachableNodes = 0;
					q.add(candidate[i]);
					visited[candidate[i]] = true;
					while(!q.isEmpty()) {
						int u = q.pop();
						for(int v : adj[u]) {
							if(!visited[v]) {
								visited[v] = true;
								q.add(v);
								reachableNodes++;
							}
						}
					}
					visited[candidate[i]] = false;
					out[candidate[i]] += reachableNodes;
					if(i < p.length - 1) out[candidate[i]] += out[p[i + 1]] + 1;
				}
			}

			/*

			Lastly, for each candidate node, if in[candidate[i]] + out[candidate[i]] is n - 2, then
			candidate[i] is semi-important.

			*/

			for(int i = 0 ; i < p.length ; i++)
				if(valid[i])
					if(in[candidate[i]] + out[candidate[i]] == n - 2)
						res++;

			/*

			Oh, and there are gay special cases, like this:

			p[1] -> p[2] -> ... -> p[k]
			          ^
			          |
			candidate[c]

			Oh and this:

			p[1] -> p[2] -> ... -> p[k]
			  ^
			  |
			candidate[c]

			Ahh, and this one:

			p[1] -> ... p[k - 1] -> p[k]
			                |
			                v
			                candidate[c]

			OH and this last one:

			p[1] -> ... p[k - 1] -> p[k]
			                          |
			                          v
			                candidate[c]

			If there are any candidates that point or get pointed from the same nodes,
			it invalidates all special cases.

			*/
			if(specialCases.size() <= 4) {
				for(int i = 0 ; i < specialCases.size() ; i++) {
					for(int j = i + 1 ; j < specialCases.size() ; j++) {
						int u = specialCases.get(i);
						int v = specialCases.get(j);
						int cmp1 = L[u] == -1 ? R[u] : L[u];
						int cmp2 = L[v] == -1 ? R[v] : L[v];
						if(cmp1 == cmp2) return 0;
					}
				}
			}
			for(int sc : specialCases) {
				Arrays.fill(visited, false);
				visited[sc] = true;
				q.add(sc);
				int incnt = 0, outcnt = 0;
				while(!q.isEmpty()) {
					int u = q.pop();
					for(int v : adj[u]) {
						if(!visited[v]) {
							visited[v] = true;
							q.add(v);
							outcnt++;
						}
					}
				}
				Arrays.fill(visited, false);
				visited[sc] = true;
				q.add(sc);
				while(!q.isEmpty()) {
					int u = q.pop();
					for(int v : rev[u]) {
						if(!visited[v]) {
							visited[v] = true;
							q.add(v);
							incnt++;
						}
					}
				}
				if(incnt + outcnt == n - 2)
					res++;
			}

			return res;
		}
		int[] dp, path;
		int go(int u) {
			if(dp[u] != -1) return dp[u];
			int res = 0;
			for(int v : adj[u]) {
				int tmp = go(v) + 1;
				if(tmp > res) {
					res = tmp;
					path[u] = v;
				}
			}
			dp[u] = res;
			return res;
		}
	}
	public CF1062F() {
		FS scan = new FS();
		int n = scan.nextInt(), e = scan.nextInt();
		int[][] edge = new int[2][e];
		for(int i = 0 ; i < e ; i++) {
			edge[0][i] = scan.nextInt() - 1;
			edge[1][i] = scan.nextInt() - 1;
		}
		Graph g = new Graph(n, edge);
		System.out.println(g.solve());
	}
	class FS {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer("");
		public String next() {
			while(!st.hasMoreTokens()) {
				try { st = new StringTokenizer(br.readLine()); }
				catch(Exception e) { e.printStackTrace(); }
			}
			return st.nextToken();
		}
		public int nextInt() { return Integer.parseInt(next()); }
	}
	public static void main(String[] args) { new CF1062F(); }
}


import java.io.*;
import java.util.*;

public class D_opt3 {

	BufferedReader br;
	PrintWriter out;
	StringTokenizer st;
	boolean eof;

	int n;
	int dn1;
	int m;

	long solve() {
		long start = System.currentTimeMillis();
		dn1 = (n + 1) * 2;
		long ans = 0;
		for (int i = 2 * (m + 1); i <= n + 1; i += 2) {
			int shiftI = (i == n + 1) ? 0 : 1;

			int tmp = dn1 - i;
			int num = (i - (m + 1)) * tmp - 1;
			int denom = i + (m + 1) * 2;

			for (int j = m + 1; j < i; j++, num -= tmp, denom += 2) {

				int k = num / denom;
				if (k >= n - m)
					k = n - m;
				if (k >= m)
					ans += (k - m) << shiftI;
			}
		}

//		System.err.println(System.currentTimeMillis() - start);
		return 3 * ans;
	}

	D_opt3() throws IOException {
		br = new BufferedReader(new InputStreamReader(System.in));
		out = new PrintWriter(System.out);
		n = nextInt();
		m = nextInt();
		out.println(solve());
		out.close();
	}

	public static void main(String[] args) throws IOException {
		new D_opt3();
	}

	String nextToken() {
		while (st == null || !st.hasMoreTokens()) {
			try {
				st = new StringTokenizer(br.readLine());
			} catch (Exception e) {
				eof = true;
				return null;
			}
		}
		return st.nextToken();
	}

	String nextString() {
		try {
			return br.readLine();
		} catch (IOException e) {
			eof = true;
			return null;
		}
	}

	int nextInt() throws IOException {
		return Integer.parseInt(nextToken());
	}

	long nextLong() throws IOException {
		return Long.parseLong(nextToken());
	}

	double nextDouble() throws IOException {
		return Double.parseDouble(nextToken());
	}
}


import java.io.*;
import java.util.*;

public class cf690B3 {

	BufferedReader br;
	PrintWriter out;
	StringTokenizer st;
	boolean eof;

	static final int SHIFT = 17;

	HashSet<Long> set;

	boolean good(long v) {
		if (set.contains(v)) {
			return false;
		}
		for (int dx = -1; dx <= 1; dx++)
			for (int dy = -1; dy <= 1; dy++) {
				if (dx == 0 && dy == 0) {
					continue;
				}
				long u = v + (dx << SHIFT) + dy;
				if (set.contains(u)) {
					return true;
				}
			}
		return false;
	}

	HashSet<Long> seen;

	void dfs(long v, HashSet<Long> cur) {
		cur.add(v);
		seen.add(v);
		for (int dx = -1; dx <= 1; dx++)
			for (int dy = -1; dy <= 1; dy++) {
				if (dx == 0 && dy == 0) {
					continue;
				}
				long u = v + (dx << SHIFT) + dy;
				if (!cur.contains(u) && good(u)) {
					dfs(u, cur);
				}
			}
	}

	boolean solve() throws IOException {
		int n = nextInt();
		int m = nextInt();
		if (n == 0 && m == 0) {
			return false;
		}

		long[] ps = new long[m];

		set = new HashSet<>();
		seen = new HashSet<>();

		int[] xs = new int[m];
		int[] ys = new int[m];

		for (int i = 0; i < m; i++) {
			xs[i] = nextInt();
			ys[i] = nextInt();
			ps[i] = ((1L * xs[i]) << SHIFT) | ys[i];
			set.add(ps[i]);
		}

		HashSet<Long> set1 = null;

		int setsCount = 0;

		for (int i = 0; i < m; i++) {
			for (int dx = -1; dx <= 1; dx++)
				for (int dy = -1; dy <= 1; dy++) {
					if (dx == 0 && dy == 0) {
						continue;
					}
					long tmp = ps[i] + (dx << SHIFT) + dy;
					if (set.contains(tmp) || seen.contains(tmp)) {
						continue;
					}

					if (++setsCount > 2) {
						throw new AssertionError(Arrays.toString(xs) + " " + Arrays.toString(ys));
					}

					HashSet<Long> cand = new HashSet<>();
					dfs(tmp, cand);

					if (set1 == null || set1.size() < cand.size()) {
						set1 = cand;
					}
				}
		}

		List<Point> a = new ArrayList<>();
		HashSet<Long> setA = new HashSet<>();
		for (int i = 0; i < m; i++) {
			for (int dx = -1; dx <= 0; dx++)
				for (int dy = -1; dy <= 0; dy++) {
					int x = xs[i] + dx;
					int y = ys[i] + dy;
					long hash = ((1L * x) << SHIFT) | y;
					if (setA.contains(hash)) {
						continue;
					}

					setA.add(hash);

					boolean ok = true;
					outer: for (int xx = 0; xx <= 1; xx++)
						for (int yy = 0; yy <= 1; yy++) {
							long hash1 = hash + (xx << SHIFT) + yy;
							if (set1.contains(hash1)) {
								ok = false;
								break outer;
							}
						}

					if (ok) {
						a.add(new Point(x, y));
					}
				}
		}

		Point[] arr = a.toArray(new Point[a.size()]);
		Point[] h = convexHull(arr);

		for (int i = 0, j = h.length - 1; i < j; i++, j--) {
			Point tmp = h[i];
			h[i] = h[j];
			h[j] = tmp;
		}

		int start = 0;
		for (int i = 1; i < h.length; i++) {
			if (h[i].x < h[start].x
					|| (h[i].x == h[start].x && h[i].y < h[start].y)) {
				start = i;
			}
		}

		out.println(h.length);
		for (int i = start; i < h.length; i++) {
			Point p = h[i];
			out.println(p.x + " " + p.y);
		}

		for (int i = 0; i < start; i++) {
			Point p = h[i];
			out.println(p.x + " " + p.y);
		}

		return true;
	}

	String asPoint(long q) {
		return "(" + (q >> SHIFT) + ", " + (q & ((1 << SHIFT) - 1)) + ")";
	}

	static class Point implements Comparable<Point> {
		static final Point ZERO = new Point(0, 0);
		int x, y;

		public Point(int x, int y) {
			this.x = x;
			this.y = y;
		}

		@Override
		public int compareTo(Point o) {
			if (y != o.y)
				return y < o.y ? -1 : 1;
			if (x != o.x)
				return x < o.x ? -1 : 1;
			return 0;
		}

		long vectMul(Point p) {
			return (long) x * p.y - (long) y * p.x;
		}

		@Override
		public String toString() {
			return "(" + x + ", " + y + ")";
		}

		public boolean equals(Point p) {
			return x == p.x && y == p.y;
		}
	}

	static int getTurn(Point A, Point B, Point C) {
		// angle between AB and AC
		long p1 = (long) (B.x - A.x) * (C.y - A.y);
		long p2 = (long) (B.y - A.y) * (C.x - A.x);
		if (p1 == p2)
			return 0;
		else
			return p1 < p2 ? -1 : 1;
	}

	/**
	 * PERMUTES POINTS IN ARRAY, consider using convexHull(a.clone()) Points
	 * themselves are modified inside method too, but changes are reverted at
	 * the end
	 *
	 * @param a
	 *            - array of points
	 * @return convexHull of these point in CCW order, no duplicates, no three
	 *         points on one side
	 */
	static Point[] convexHull(Point[] a) {

		if (a == null)
			return null;
		if (a.length == 0)
			return new Point[] {};

		int st = 0;
		for (int i = 1; i < a.length; i++)
			if (a[i].compareTo(a[st]) < 0)
				st = i;

		Point tmp = a[0];
		a[0] = a[st];
		a[st] = tmp;

		int sx = a[0].x;
		int sy = a[0].y;

		for (int i = 0; i < a.length; i++) {
			a[i].x -= sx;
			a[i].y -= sy;
		}

		Arrays.sort(a, 1, a.length, new Comparator<Point>() {

			@Override
			public int compare(Point o1, Point o2) {
				if (o1.equals(o2))
					return 0;
				if (o1.equals(Point.ZERO)) {
					return -1;
				}
				if (o2.equals(Point.ZERO)) {
					return 1;
				}
				long val = o1.vectMul(o2);
				if (val != 0)
					return val > 0 ? -1 : 1;
				if (o1.y == 0) {
					return o1.x < o2.x ? -1 : 1;
				} else {
					return o1.y < o2.y ? -1 : 1;
				}
			}
		});

		Point[] h = new Point[a.length];
		int sz = 0;
		h[sz++] = a[0];
		for (int i = 1; i < a.length; i++) {
			if (a[i].equals(h[sz - 1]))
				continue;
			while (sz > 1 && getTurn(h[sz - 2], h[sz - 1], a[i]) <= 0)
				sz--;
			h[sz++] = a[i];
		}

		for (int i = 0; i < a.length; i++) {
			a[i].x += sx;
			a[i].y += sy;
		}

		return Arrays.copyOf(h, sz);
	}

	cf690B3() throws IOException {
		br = new BufferedReader(new InputStreamReader(System.in));
		out = new PrintWriter(System.out);
		while (solve())
			;
		out.close();
	}

	public static void main(String[] args) throws IOException {
		new cf690B3();
	}

	String nextToken() {
		while (st == null || !st.hasMoreTokens()) {
			try {
				st = new StringTokenizer(br.readLine());
			} catch (Exception e) {
				eof = true;
				return null;
			}
		}
		return st.nextToken();
	}

	String nextString() {
		try {
			return br.readLine();
		} catch (IOException e) {
			eof = true;
			return null;
		}
	}

	int nextInt() throws IOException {
		return Integer.parseInt(nextToken());
	}

	long nextLong() throws IOException {
		return Long.parseLong(nextToken());
	}

	double nextDouble() throws IOException {
		return Double.parseDouble(nextToken());
	}
}


import java.io.*;
import java.math.BigInteger;
import java.util.*;

public class F {
	FastScanner in;
	PrintWriter out;

	void solve() {
		int n = in.nextInt();
		int[][] f = new int[2 * n][n];
		for (int i = 0; i < n; i++) {
			f[0][i] = in.nextInt() - 1;
		}
		for (int i = 0; i < n; i++) {
			for (int j = 1; j < 2 * n; j++) {
				f[j][i] = f[0][f[j - 1][i]];
			}
		}
		BigInteger res = BigInteger.ONE;
		for (int i = 0; i < n; i++) {
			for (int pw = 0; pw < 2 * n; pw++) {
				if (f[pw][f[pw][i]] == f[pw][i]) {
				//	out.println(pw + 1);
					BigInteger g = BigInteger.valueOf(pw + 1).gcd(res);
					res = res.multiply(BigInteger.valueOf(pw + 1)).divide(g);
					break;
				}
			}
		}
		out.println(res);
	}

	void run() {
		in = new FastScanner();
		out = new PrintWriter(System.out);

		solve();

		out.close();
	}

	class FastScanner {
		BufferedReader br;
		StringTokenizer st;

		public FastScanner() {
			br = new BufferedReader(new InputStreamReader(System.in));
		}

		String next() {
			while (st == null || !st.hasMoreTokens()) {
				try {
					st = new StringTokenizer(in.br.readLine());
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			return st.nextToken();
		}

		int nextInt() {
			return Integer.parseInt(next());
		}

		long nextLong() {
			return Long.parseLong(next());
		}

		double nextDouble() {
			return Double.parseDouble(next());
		}
	}

	public static void main(String[] args) {
		new F().run();
	}
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringTokenizer;

public class Div1_411F {

	int numTV;
	int A;
	int B;

	ArrayList<Integer>[] tAList;

	boolean[][] tVAct;
	int[] numM;

	int cTime = 0;
	int[] disc;
	int[] low;

	ArrayDeque<Integer> stack = new ArrayDeque<Integer>();

	ArrayList<ArrayList<Integer>> sCCs = new ArrayList<ArrayList<Integer>>();
	int[] comp;

	int numC;

	// may contain repeats
	ArrayList<Integer>[] cAList;

	boolean[] visited;
	ArrayList<Integer> tOrder = new ArrayList<Integer>();

	boolean[][] compAct;

	// represents the minimum and the maximum amounts of active members a tournament vertex can have
	int[] min;
	int[] max;

	long MOD = 1_000_000_007;

	int[][] comb;

	void dfs(int cV) {
		disc[cV] = low[cV] = cTime++;
		stack.push(cV);

		for (int adj : tAList[cV]) {
			if (disc[adj] == -1) {
				dfs(adj);
			}
			if (low[adj] < low[cV]) {
				low[cV] = low[adj];
			}
		}
		if (disc[cV] == low[cV]) {
			ArrayList<Integer> cSCC = new ArrayList<Integer>();
			int cCInd = sCCs.size();
			while (stack.peek() != cV) {
				low[stack.peek()] = Integer.MAX_VALUE;
				comp[stack.peek()] = cCInd;
				cSCC.add(stack.pop());
			}
			low[stack.pop()] = Integer.MAX_VALUE;
			comp[cV] = cCInd;
			cSCC.add(cV);
			sCCs.add(cSCC);
		}
	}

	@SuppressWarnings("unchecked")
	void findSCCs() {
		disc = new int[numTV];
		low = new int[numTV];
		comp = new int[numTV];
		Arrays.fill(disc, -1);

		for (int i = 0; i < numTV; i++) {
			if (disc[i] == -1) {
				dfs(i);
			}
		}

		numC = sCCs.size();

		cAList = new ArrayList[numC];
		for (int i = 0; i < numC; i++) {
			cAList[i] = new ArrayList<Integer>();
		}
		for (int cV = 0; cV < numTV; cV++) {
			for (int adj : tAList[cV]) {
				cAList[comp[cV]].add(comp[adj]);
			}
		}
	}

	int fGCD(int a, int b) {
		while (b != 0) {
			int t = b;
			b = a % b;
			a = t;
		}
		return a;
	}

	void condenseComps() {
		compAct = new boolean[numC][];
		for (int cComp = 0; cComp < numC; cComp++) {
			ArrayList<Integer> cMems = sCCs.get(cComp);
			int cGCD = numM[cMems.get(0)];
			for (int j : cMems) {
				cGCD = fGCD(cGCD, numM[j]);
			}
			boolean[] cCompSet = compAct[cComp] = new boolean[cGCD];
			for (int cM : cMems) {
				for (int sM = 0; sM < numM[cM]; sM++) {
					if (tVAct[cM][sM]) {
						cCompSet[sM % cGCD] = true;
					}
				}
			}
		}
	}

	// topologically order the transitive tournament graph
	void tOrder(int cV) {
		visited[cV] = true;
		for (int adj : cAList[cV]) {
			if (!visited[adj]) {
				tOrder(adj);
			}
		}
		tOrder.add(cV);
	}

	void orderComps() {
		visited = new boolean[numC];
		for (int i = 0; i < numC; i++) {
			if (!visited[i]) {
				tOrder(i);
			}
		}
		Collections.reverse(tOrder);
	}

	void pushConnections() {
		boolean[] cSet = compAct[tOrder.get(0)];

		for (int cInd = 1; cInd < numC; cInd++) {
			boolean[] pSet = cSet;
			int cComp = tOrder.get(cInd);
			cSet = compAct[cComp];
			int gcd = fGCD(pSet.length, cSet.length);

			boolean[] resSet = new boolean[gcd];

			for (int i = 0; i < pSet.length; i++) {
				if (pSet[i]) {
					resSet[i % gcd] = true;
				}
			}

			for (int cRes = 0; cRes < gcd; cRes++) {
				for (int cNum = cRes; cNum < cSet.length; cNum += gcd) {
					cSet[cNum] = true;
				}
			}
		}
	}

	void findMaxs() {
		max = new int[numTV];

		for (int cComp = 0; cComp < numC; cComp++) {
			int aCnt = 0;
			for (boolean b : compAct[cComp]) {
				if (b) {
					aCnt++;
				}
			}
			for (int cM : sCCs.get(cComp)) {
				max[cM] = numM[cM] / compAct[cComp].length * aCnt;
			}
		}
	}

	// uses Pascal's identity
	void preComb() {
		comb = new int[numTV + 1][numTV + 1];
		for (int i = 0; i <= numTV; i++) {
			comb[i][0] = 1;
			for (int j = 1; j <= i; j++) {
				comb[i][j] = comb[i - 1][j] + comb[i - 1][j - 1];
				if (comb[i][j] >= 1_000_000_007) {
					comb[i][j] -= 1_000_000_007;
				}
			}
		}
	}

	long findAns() {
		long ans = 0;

		for (int sLws = 0; sLws < numTV; sLws++) {
			int sGrt = 0;
			int pGrt = 0;
			for (int oth = 0; oth < numTV; oth++) {
				if (oth != sLws) {
					if (min[oth] > max[sLws]) {
						if (++sGrt >= A) {
							continue;
						}
					}

					// secondary condition in case of equality to prevent overcounting
					else if (max[oth] > max[sLws] || (max[oth] == max[sLws] && sLws < oth)) {
						pGrt++;
					}
				}
			}

			for (int numT = 0; numT < B && numT <= pGrt && numT + sGrt < A; numT++) {
				ans = (ans + ((long) comb[pGrt][numT]) * comb[sGrt][B - 1 - numT]) % MOD;
			}
		}

		return ans;
	}

	@SuppressWarnings("unchecked")
	void execute() throws IOException {
		BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

		StringTokenizer inputData = new StringTokenizer(reader.readLine());
		numTV = Integer.parseInt(inputData.nextToken());
		A = Integer.parseInt(inputData.nextToken());
		B = Integer.parseInt(inputData.nextToken());

		tAList = new ArrayList[numTV];

		for (int i = 0; i < numTV; i++) {
			tAList[i] = new ArrayList<Integer>();
			String nLine = reader.readLine();
			for (int j = 0; j < numTV; j++) {
				if (nLine.charAt(j) == '1') {
					tAList[i].add(j);
				}
			}
		}

		tVAct = new boolean[numTV][];
		numM = new int[numTV];
		min = new int[numTV];

		for (int i = 0; i < numTV; i++) {
			inputData = new StringTokenizer(reader.readLine());
			int cNumM = numM[i] = Integer.parseInt(inputData.nextToken());
			tVAct[i] = new boolean[cNumM];

			String nLine = inputData.nextToken();
			for (int j = 0; j < cNumM; j++) {
				tVAct[i][j] = nLine.charAt(j) == '1';
				if (tVAct[i][j]) {
					min[i]++;
				}
			}
		}
		reader.close();

		findSCCs();
		condenseComps();
		orderComps();
		pushConnections();
		findMaxs();
		preComb();

		long ans = findAns();
		System.out.println(ans);
	}

	public static void main(String[] args) throws IOException {
		new Div1_411F().execute();
	}

}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.PrintStream;
import java.util.InputMismatchException;
import java.io.IOException;
import java.util.ArrayList;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author Coupon Kaka
 */
/*
    "Digits 1-9 in unit place is a myth, actual counting always has digit 0 in the unit place." - Coupon Kaka
*/
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        FCoffeeVarietiesEasyVersion solver = new FCoffeeVarietiesEasyVersion();
        solver.solve(1, in, out);
        out.close();
    }

    static class FCoffeeVarietiesEasyVersion {
        boolean isNew(int ind, InputReader sc) {
            System.out.println("? " + (ind + 1));
            System.out.flush();
            char ans = sc.next().charAt(0);
            if (ans == 'Y')
                return false;
            return true;
        }

        void reset() {
            System.out.println("R");
            System.out.flush();
        }

        public void solve(int testNumber, InputReader sc, PrintWriter out) {
            int n = sc.nextInt();
            int k = sc.nextInt();

            int flag[] = new int[n];
            ArrayList<Integer> list = new ArrayList<>();
            for (int i = 0; i < k; ++i) {
                if (isNew(i, sc)) {
                    flag[i] = 0;
                    list.add(i);
                } else
                    flag[i] = 1;
            }

            for (int i = k; i < n; i += (k + 1) / 2) {
                int l = i;
                int r = min(n - 1, i + ((k + 1) / 2) - 1);

                for (int j = 0; j < list.size(); j += (k + 1) / 2) {
                    reset();
                    for (int p = j; p < min(list.size(), j + ((k + 1) / 2)); ++p)
                        isNew(list.get(p), sc);
                    for (int p = l; p <= r; ++p) {
                        if (!isNew(p, sc))
                            flag[p] = 1;
                    }
                }

                for (int j = l; j <= r; ++j) {
                    if (flag[j] == 0)
                        list.add(j);
                }
            }

            System.out.println("! " + list.size());
            System.out.flush();
        }

    }

    static class InputReader {
        private InputStream stream;
        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;
        private InputReader.SpaceCharFilter filter;

        public InputReader(InputStream stream) {
            this.stream = stream;
        }

        public int read() {
            if (numChars == -1)
                throw new InputMismatchException();

            if (curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = stream.read(buf);
                } catch (IOException e) {
                    throw new InputMismatchException();
                }

                if (numChars <= 0)
                    return -1;
            }
            return buf[curChar++];
        }

        public int nextInt() {
            int c = read();

            while (isSpaceChar(c))
                c = read();

            int sgn = 1;

            if (c == '-') {
                sgn = -1;
                c = read();
            }

            int res = 0;
            do {
                if (c < '0' || c > '9')
                    throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = read();
            }
            while (!isSpaceChar(c));

            return res * sgn;
        }

        public String readString() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            StringBuilder res = new StringBuilder();
            do {
                res.appendCodePoint(c);
                c = read();
            }
            while (!isSpaceChar(c));

            return res.toString();
        }

        public boolean isSpaceChar(int c) {
            if (filter != null)
                return filter.isSpaceChar(c);
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

        public String next() {
            return readString();
        }

        public interface SpaceCharFilter {
            public boolean isSpaceChar(int ch);

        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Set;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.TreeSet;
import java.util.ArrayList;
import java.util.List;
import java.util.AbstractCollection;
import java.util.TreeMap;
import java.util.StringTokenizer;
import java.util.Map;
import java.io.BufferedReader;
import java.util.Collections;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        FastScanner in = new FastScanner(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        TaskC solver = new TaskC();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskC {
        final double eps = 1e-8;

        public void solve(int testNumber, FastScanner in, PrintWriter out) {
            int n = in.nextInt();
            Circle[] circles = new Circle[n];
            for (int i = 0; i < n; i++) {
                int x = in.nextInt();
                int y = in.nextInt();
                int r = in.nextInt();
                circles[i] = new Circle();
                circles[i].o = new Point(x, y);
                circles[i].r = r;
            }

            Set<Point> all = new TreeSet<>();
            for (int i = 0; i < n; i++) {
                for (int j = i + 1; j < n; j++) {
                    List<Point> inter = intersect(circles[i], circles[j]);
                    if (inter == null) {
                        continue;
                    }
                    all.addAll(inter);
                }
            }

            Map<Point, Integer> pointId = new TreeMap<>();
            List<Set<Integer>> adj = new ArrayList<>();
            for (int i = 0; i < n; i++) {
                List<Double> angles = new ArrayList<>();
                final int K = 100;
                for (int j = 0; j < K; j++) {
                    angles.add(Math.PI * 2 * j / K);
                }
                for (Point p : all) {
                    if (circles[i].contains(p)) {
                        double dx = p.x - circles[i].o.x;
                        double dy = p.y - circles[i].o.y;
                        double alpha = Math.atan2(dy, dx);
                        if (alpha < 0) {
                            alpha += 2 * Math.PI;
                        }
                        angles.add(alpha);
                    }
                }
                Collections.sort(angles);
                Point dir = new Point(1, 0).scale(circles[i].r);
                Double[] as = new TreeSet<>(angles).toArray(new Double[0]);
                for (int j = 0; j < as.length; j++) {
                    Point p = circles[i].o.add(dir.rotate(as[j]));
                    getId(p, pointId, adj);
                }
                for (int j = 0; j < as.length; j++) {
                    Point p = circles[i].o.add(dir.rotate(as[j]));
                    Point q = circles[i].o.add(dir.rotate(as[(j + 1) % as.length]));
                    int u = pointId.get(p);
                    int v = pointId.get(q);
                    if (u == v) {
                        continue;
                    }
                    adj.get(u).add(v);
                    adj.get(v).add(u);
                }
            }

            int v = adj.size();
            int e = 0;
            for (int i = 0; i < v; i++) {
                e += adj.get(i).size();
            }
            e /= 2;

            int comps = 0;
            boolean[] was = new boolean[v];
            for (int i = 0; i < v; i++) {
                if (!was[i]) {
                    ++comps;
                    dfs(i, was, adj);
                }
            }

            out.println(1 + comps + e - v);
        }

        private void dfs(int i, boolean[] was, List<Set<Integer>> adj) {
            was[i] = true;
            for (int j : adj.get(i)) {
                if (!was[j]) {
                    dfs(j, was, adj);
                }
            }
        }

        private void getId(Point p, Map<Point, Integer> pointId, List<Set<Integer>> adj) {
            if (!pointId.containsKey(p)) {
                int sz = pointId.size();
                pointId.put(p, sz);
                adj.add(new TreeSet<>());
            }
        }

        private List<Point> intersect(Circle c1, Circle c2) {
            double dx = c1.o.x - c2.o.x;
            double dy = c1.o.y - c2.o.y;
            double d = Math.sqrt(dx * dx + dy * dy);
            double cos = (c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d);
            if (cos < -1 - eps) {
                return null;
            }
            if (cos < -1) {
                cos = -1;
            }
            if (cos > 1 + eps) {
                return null;
            }
            if (cos > 1) {
                cos = 1;
            }
            List<Point> res = new ArrayList<>();
            double alpha = Math.acos(cos);
            Point dir = c2.o.sub(c1.o).norm();
            res.add(c1.o.add(dir.scale(c1.r).rotate(+alpha)));
            res.add(c1.o.add(dir.scale(c1.r).rotate(-alpha)));
            return res;
        }

        class Point implements Comparable<Point> {
            double x;
            double y;

            Point(double x, double y) {
                this.x = x;
                this.y = y;
            }

            Point add(Point o) {
                return new Point(x + o.x, y + o.y);
            }

            Point sub(Point o) {
                return new Point(x - o.x, y - o.y);
            }

            Point rotate(double alpha) {
                double c = Math.cos(alpha);
                double s = Math.sin(alpha);
                double nx = x * c - y * s;
                double ny = x * s + y * c;
                return new Point(nx, ny);
            }

            Point norm() {
                double len = length();
                return scale(1.0 / len);
            }

            Point scale(double f) {
                return new Point(f * x, f * y);
            }

            double length() {
                return Math.sqrt(x * x + y * y);
            }

            public String toString() {
                return String.format("%.6f %.6f", x, y);
            }

            public boolean equals(Object obj) {
                Point o = (Point) obj;
                return Math.abs(x - o.x) < eps && Math.abs(y - o.y) < eps;
            }

            public int compareTo(Point o) {
                if (Math.abs(x - o.x) > eps) {
                    return x < o.x ? -1 : 1;
                }
                if (Math.abs(y - o.y) > eps) {
                    return y < o.y ? -1 : 1;
                }
                return 0;
            }

        }

        class Circle {
            Point o;
            double r;

            boolean contains(Point p) {
                double dist = p.sub(o).length();
                return Math.abs(dist - r) < eps;
            }

        }

    }

    static class FastScanner {
        private BufferedReader in;
        private StringTokenizer st;

        public FastScanner(InputStream stream) {
            in = new BufferedReader(new InputStreamReader(stream));
        }

        public String next() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(in.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return st.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

    }
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class bubblestrike {
    public static void main(String[] args) throws IOException {
        BufferedReader f = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(f.readLine());
        int x = Integer.parseInt(st.nextToken());
        double prob = Double.parseDouble(st.nextToken());
        int pos = x;
        for(int a = x; a>=1; a/=2){
            while (check(pos-a,x-(pos-a),prob)){
                pos-=a;
            }
        }
        System.out.println(pos);

    }
    public static boolean check (int good, int bad, double proba){
        double g=good+0.0;
        double b=bad+0.0;
        double ans = (0.25*b*(b-1.0)+(g*(g-1)*(g-2)/6.0)+b*0.5*(g)*(g-1))/((g+b)*(g+b-1)*(g+b-2)/6.0);

        return ans >=proba;
    }
}