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

public class C {

	static final int INF = Integer.MAX_VALUE / 3;
	BufferedReader br;
	PrintWriter out;
	StringTokenizer st;
	boolean eof;

	static int N = 1_000_000;
	static int ROOT = 1_000;
	static boolean[] isPrime = new boolean[N + 1];
	static List<Integer> primes = new ArrayList<>(78498);
	static {
		Arrays.fill(isPrime, true);
		for (int i = 2; i <= N; i++) {
			if (isPrime[i]) {
				primes.add(i);
				if (i <= ROOT) {
					for (int j = i * i; j <= N; j += i) {
						isPrime[j] = false;
					}
				}
			}
		}
	}

	int n;

	void solve() throws IOException {
		n = nextInt();
		long[] a = new long[n];

		for (int i = 0; i < n; i++) {
			a[i] = nextLong();
		}

		dp = new int[1 << n][n + 2];
		// [0..n) - root
		// n - no root
		// n + 1 - min of all

		for (int mask = 1; mask < (1 << n); mask++) {
			Arrays.fill(dp[mask], INF);
			if (Integer.bitCount(mask) == 1) {
				int pos = Integer.numberOfTrailingZeros(mask);
				int val = sumPrimePowers(a[pos]);
				if (val > 1)
					val++;
				dp[mask][pos] = dp[mask][n + 1] = val;
				continue;
			}
			for (int root = 0; root < n; root++) {
				if (test(mask, root)) {
					int noRoot = mask ^ (1 << root);
					for (int sub = noRoot; sub > 0; sub = (sub - 1) & noRoot) {
						long rest = a[root];
						boolean badSub = false;

//						if (mask == 14 && root == 1 && sub == 4) {
//							System.err.println("?");
//						}

						for (int i = 0; i < n; i++) {
							if (test(sub, i)) {
								if (rest % a[i] == 0) {
									rest /= a[i];
								} else {
									badSub = true;
									break;
								}
							}
						}

						if (badSub)
							continue;

						int add = 1 + sumPrimePowers(rest);
						if (add == 1)
							add = 0;
						dp[mask][root] = Math.min(dp[mask][root],
								auxDP(noRoot, sub) + add);
					}
					dp[mask][n + 1] = Math.min(dp[mask][n + 1], dp[mask][root]);
				}
			}

			dp[mask][n] = auxDp(mask) + 1;
			dp[mask][n + 1] = Math.min(dp[mask][n + 1], dp[mask][n]);
		}

		out.println(dp[(1 << n) - 1][n + 1]);
	}

	int[][] dp;

	int auxDp(int tot) {
		int[] curDp = new int[1 << n];
		curDp[0] = 0;
		for (int mask = 1; mask <= tot; mask++) {
			if ((mask & tot) != mask)
				continue;
			int start = (mask == tot) ? ((tot - 1) & tot) : mask;
			curDp[mask] = INF;
			for (int sub = start; sub > 0; sub = (sub - 1) & mask) {
				curDp[mask] = Math.min(curDp[mask], dp[sub][n + 1]
						+ curDp[mask ^ sub]);
			}
		}
		return curDp[tot];
	}

	int auxDP(int tot, int roots) {
		if ((tot & roots) != roots)
			throw new AssertionError();
		int[] curDp = new int[1 << n];
		int[] newDp = new int[1 << n];
		Arrays.fill(curDp, INF);
		curDp[0] = 0;
		for (int pos = 0; pos < n; pos++) {
			if (test(roots, pos)) {
				Arrays.fill(newDp, INF);

				for (int newMask = 0; newMask < (1 << n); newMask++) {
					if ((tot & newMask) != newMask || !test(newMask, pos))
						continue;
					for (int posMask = newMask; posMask > 0; posMask = (posMask - 1)
							& newMask) {
						if (!test(posMask, pos))
							continue;
						int rest = newMask ^ posMask;
//						if (tot == 3 && roots == 2) {
//							System.err.println(newMask + " -> " + posMask + " "
//									+ rest);
//							System.err.println(curDp[rest] + " "
//									+ dp[posMask][pos]);
//						}
						newDp[newMask] = Math.min(newDp[newMask], curDp[rest]
								+ dp[posMask][pos]);
					}
				}
				int[] tmp = curDp;
				curDp = newDp;
				newDp = tmp;
			}
		}
		return curDp[tot];
	}

	static boolean test(int mask, int i) {
		return ((mask >> i) & 1) == 1;
	}

	int sumPrimePowers(long x) {
		int ret = 0;
		for (int i = 0; i < primes.size(); i++) {
			int p = primes.get(i);
			while (x % p == 0) {
				x /= p;
				ret++;
			}
		}
		if (x != 1)
			ret++;
		return ret;
	}

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

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

	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.*;
import java.math.*;

public class C implements Runnable {
	static BufferedReader in;
	static PrintWriter out;
	static StringTokenizer st;
	static Random rnd;

	private void solve() throws IOException {
		int numbersCount = nextInt();
		long[] numbers = new long[numbersCount];
		for (int i = 0; i < numbersCount; i++)
			numbers[i] = nextLong();
		for (int i = 1; i < numbersCount; i++) {
			int j = rnd.nextInt(i);
			long a = numbers[i];
			numbers[i] = numbers[j];
			numbers[j] = a;
		}

		rec(0, numbersCount, numbers, new boolean[numbersCount], 0, 0,
				new long[numbersCount], new boolean[numbersCount]);

		out.println(result);
	}

	int result = Integer.MAX_VALUE;

	private void rec(int step, int numbersCount, long[] numbers,
			boolean[] used, int treesNearRoot, int treesCount,
			long[] treesRoots, boolean[] isLeaf) {
		int currentVertexes = getVertexes(treesNearRoot, treesCount,
				treesRoots, isLeaf);

//		out.println(currentVertexes + " " + result + " " + step + " "
//				+ treesNearRoot + " " + treesCount + " "
//				+ Arrays.toString(treesRoots));

		if (currentVertexes >= result)
			return;

		if (step == numbersCount) {
			result = Math.min(result, currentVertexes);
		} else {
			for (int i = 0; i < numbersCount; i++) {
				if (!used[i]) {
					used[i] = true;
					long number = numbers[i];
					boolean end = false;

					// add tree
					{
						treesRoots[treesCount] = number;
						isLeaf[treesCount] = true;
						rec(step + 1, numbersCount, numbers, used,
								treesNearRoot + 1, treesCount + 1, treesRoots,
								isLeaf);

						if (currentVertexes >= result)
							end = true;
					}

					// add to another tree
					for (int j = 0; j < treesCount && !end; j++) {
						if (treesRoots[j] % number == 0) {
							treesRoots[j] /= number;
							boolean backUpIsLeaf = isLeaf[j];
							isLeaf[j] = false;
							treesRoots[treesCount] = number;
							isLeaf[treesCount] = true;

							rec(step + 1, numbersCount, numbers, used,
									treesNearRoot, treesCount + 1, treesRoots,
									isLeaf);

							treesRoots[j] *= number;
							isLeaf[j] = backUpIsLeaf;

							if (currentVertexes >= result)
								end = true;
						}
					}

					used[i] = false;
				}
			}
		}
	}

	private int getVertexes(int treesNearRoot, int treesCount,
			long[] treesRoots, boolean[] isLeaf) {
		int vertexes = (treesNearRoot > 1 ? 1 : 0);
		for (int i = 0; i < treesCount; i++) {
			int childs = getPrimes(treesRoots[i]);
			int add = (childs <= 1 && isLeaf[i]) ? 1 : (1 + childs);
			vertexes += add;
		}
		return vertexes;
	}

	Map<Long, Integer> cache = new HashMap<Long, Integer>();

	private int getPrimes(long n) {
		if (n == 1)
			return 0;
		Integer cacheValue = cache.get(n);
		if (cacheValue != null)
			return cacheValue;
		int result = 1;
		for (long i = 2; i * i <= n; i++) {
			if (n % i == 0) {
				result = 1 + getPrimes(n / i);
				break;
			}
		}
		cache.put(n, result);
		return result;
	}

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

	public void run() {
		try {
			in = new BufferedReader(new InputStreamReader(System.in));
			out = new PrintWriter(System.out);

			rnd = new Random();

			solve();

			out.close();
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}
	}

	private String nextToken() throws IOException {
		while (st == null || !st.hasMoreTokens()) {
			String line = in.readLine();

			if (line == null)
				return null;

			st = new StringTokenizer(line);
		}

		return st.nextToken();
	}

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

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

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


//package round167;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.InputMismatchException;
import java.util.Queue;

public class C2 {
	InputStream is;
	PrintWriter out;
	String INPUT = "";

	void solve()
	{
		int n = ni(), m = ni();
		int[] from = new int[m];
		int[] to = new int[m];
		for(int i = 0;i < m;i++){
			from[i] = ni()-1;
			to[i] = ni()-1;
		}
		int[][] g = packU(n, from, to);
		int[] col = new int[n];
		Queue<Integer> q = new ArrayDeque<Integer>();
		for(int i = 0;i < n;i++){
			q.add(i);
		}
		while(!q.isEmpty()){
			int cur = q.poll();
			int[] ct = new int[2];
			for(int e : g[cur]){
				ct[col[e]]++;
			}
			if(ct[col[cur]] >= 2){
				col[cur] = 1-col[cur];
				for(int e : g[cur]){
					q.add(e);
				}
			}
		}
		for(int i = 0;i < n;i++){
			out.print(col[i]);
		}
		out.println();
	}

	static int[][] packU(int n, int[] from, int[] to) {
		int[][] g = new int[n][];
		int[] p = new int[n];
		for(int f : from)
			p[f]++;
		for(int t : to)
			p[t]++;
		for(int i = 0;i < n;i++)
			g[i] = new int[p[i]];
		for(int i = 0;i < from.length;i++){
			g[from[i]][--p[from[i]]] = to[i];
			g[to[i]][--p[to[i]]] = from[i];
		}
		return g;
	}

	void run() throws Exception
	{
		is = oj ? System.in : new ByteArrayInputStream(INPUT.getBytes());
		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 C2().run(); }

	private byte[] inbuf = new byte[1024];
	private int lenbuf = 0, ptrbuf = 0;

	private int readByte()
	{
		if(lenbuf == -1)throw new InputMismatchException();
		if(ptrbuf >= lenbuf){
			ptrbuf = 0;
			try { lenbuf = is.read(inbuf); } catch (IOException e) { throw new InputMismatchException(); }
			if(lenbuf <= 0)return -1;
		}
		return inbuf[ptrbuf++];
	}

	private boolean isSpaceChar(int c) { return !(c >= 33 && c <= 126); }
	private int skip() { int b; while((b = readByte()) != -1 && isSpaceChar(b)); return b; }

	private double nd() { return Double.parseDouble(ns()); }
	private char nc() { return (char)skip(); }

	private String ns()
	{
		int b = skip();
		StringBuilder sb = new StringBuilder();
		while(!(isSpaceChar(b))){ // when nextLine, (isSpaceChar(b) && b != ' ')
			sb.appendCodePoint(b);
			b = readByte();
		}
		return sb.toString();
	}

	private char[] ns(int n)
	{
		char[] buf = new char[n];
		int b = skip(), p = 0;
		while(p < n && !(isSpaceChar(b))){
			buf[p++] = (char)b;
			b = readByte();
		}
		return n == p ? buf : Arrays.copyOf(buf, p);
	}

	private char[][] nm(int n, int m)
	{
		char[][] map = new char[n][];
		for(int i = 0;i < n;i++)map[i] = ns(m);
		return map;
	}

	private int[] na(int n)
	{
		int[] a = new int[n];
		for(int i = 0;i < n;i++)a[i] = ni();
		return a;
	}

	private int ni()
	{
		int num = 0, b;
		boolean minus = false;
		while((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
		if(b == '-'){
			minus = true;
			b = readByte();
		}

		while(true){
			if(b >= '0' && b <= '9'){
				num = num * 10 + (b - '0');
			}else{
				return minus ? -num : num;
			}
			b = readByte();
		}
	}

	private long nl()
	{
		long num = 0;
		int b;
		boolean minus = false;
		while((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
		if(b == '-'){
			minus = true;
			b = readByte();
		}

		while(true){
			if(b >= '0' && b <= '9'){
				num = num * 10 + (b - '0');
			}else{
				return minus ? -num : num;
			}
			b = readByte();
		}
	}

	private boolean oj = System.getProperty("ONLINE_JUDGE") != null;
	private void tr(Object... o) { if(!oj)System.out.println(Arrays.deepToString(o)); }
}


import java.io.BufferedReader;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.StringTokenizer;

public class CF272E {

	final int maxn = 300030;
	ArrayList[] adj = new ArrayList[maxn];
	boolean[] vis = new boolean[maxn];
	int[] color = new int[maxn];
	FastIO scan = new FastIO();
	PrintWriter cout = new PrintWriter(System.out);

	void dfs(int u) {
		vis[u] = true;
		int cnt = 0;
		for (int i = 0; i < adj[u].size(); i++) {
			int v = (int) adj[u].get(i);
			if (color[u] == color[v]) {
				cnt++;
			}
		}
		if (cnt >= 2) {
			color[u] = 1 - color[u];
			for (int i = 0; i < adj[u].size(); i++) {
				int v = (int) adj[u].get(i);
				dfs(v);
			}
		}
	}

	void run() {
		for (int i = 0; i < maxn; i++)
			adj[i] = new ArrayList<Integer>();
		int n = scan.nextInt();
		int m = scan.nextInt();
		for (int i = 0; i < m; i++) {
			int u = scan.nextInt();
			int v = scan.nextInt();
			adj[u].add(v);
			adj[v].add(u);
		}
		for (int i = 1; i <= n; i++) {
			if (vis[i] == false)
				dfs(i);
		}
		for (int i = 1; i <= n; i++)
			cout.print(color[i]);
		cout.println();
		cout.flush();
	}

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

	public class FastIO {
		BufferedReader br;
		StringTokenizer st;
		PrintWriter out;

		public FastIO(InputStream in, OutputStream o) {
			br = new BufferedReader(new InputStreamReader(in));
			out = new PrintWriter(new OutputStreamWriter(o));
			eat("");
		}

		public FastIO() {
			br = new BufferedReader(new InputStreamReader(System.in));
			out = new PrintWriter(new OutputStreamWriter(System.out));
			eat("");
		}

		public void eat(String s) {
			st = new StringTokenizer(s);
		}

		public String nextLine() {
			try {
				return br.readLine();
			} catch (IOException e) {
				throw new IOError(e);
			}
		}

		public boolean hasNext() {
			while (!st.hasMoreTokens()) {
				String s = nextLine();
				if (s == null)
					return false;
				eat(s);
			}
			return true;
		}

		public String next() {
			hasNext();
			return st.nextToken();
		}

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

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

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

		public void print(String ans) {
			out.print(ans);
			out.flush();
		}

		public void println(String ans) {
			out.println(ans);
			out.flush();
		}

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

	}
}


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.StringTokenizer;

public class TC
{
    static int N;
    static ArrayList<Integer>[] adjMat;
    static int[] col;

    static boolean verify()
    {
        boolean isDone=true;

        for( int i=1; i<=N; i++ )
        {
            int cnt=0;

            for( int nbr:adjMat[i])
            {
                if( col[nbr] == col[i])
                    cnt++;
            }

            if( cnt >=2 )
            {
                isDone=false;
                col[i]=1-col[i];
            }

        }

        return isDone;
    }

    static void solve() throws IOException
    {
        col=new int[N+1];
        Arrays.fill( col, -1 );

        for( int i=1; i<=N; i++ )
        {
            int c1=0, c2=0;

            for( int nbr:adjMat[i])
            {
                if( col[nbr] == 0 )
                    c1++;

                if( col[nbr] == 1 )
                    c2++;
            }

            if( c1<=1 )
                col[i]=0;

            else
                col[i]=1;
        }

        boolean isValid=false;

        while( !isValid )
        {
            isValid=verify();
        }

        BufferedWriter out=new BufferedWriter( new OutputStreamWriter( System.out ));

        for( int i=1; i<=N; i++ )
            out.write( Integer.toString( col[i]));

        out.write("\n");
        out.close();
    }

    public static void main( String[] args ) throws IOException
    {
        BufferedReader br=new BufferedReader( new InputStreamReader( System.in ) );
        String s=br.readLine();
        StringTokenizer tok=new StringTokenizer( s );
        N=Integer.parseInt( tok.nextToken() );
        int m=Integer.parseInt( tok.nextToken() );
        adjMat=new ArrayList[N+1];
        for( int i=0; i<N+1; i++ )
            adjMat[i]=new ArrayList();
        while( m-- > 0 )
        {
            s=br.readLine();
            tok=new StringTokenizer( s );
            int a=Integer.parseInt( tok.nextToken() );
            int b=Integer.parseInt( tok.nextToken() );
            adjMat[a].add(b);
            adjMat[b].add(a);
        }
        solve();

    }
}


import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.SortedSet;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.TreeSet;

/**
 * #
 * @author pttrung
 */
public class E_Round_167_Div2 {

    public static long MOD = 1000000007;
    static int time = 0;

    public static void main(String[] args) throws FileNotFoundException {
        // PrintWriter out = new PrintWriter(new FileOutputStream(new File(
        // "output.txt")));
        PrintWriter out = new PrintWriter(System.out);
        Scanner in = new Scanner();
        int n = in.nextInt();
        int m = in.nextInt();
        ArrayList<Integer>[] map = new ArrayList[n];
        for (int i = 0; i < n; i++) {
            map[i] = new ArrayList();
        }
        for (int i = 0; i < m; i++) {
            int a = in.nextInt() - 1;
            int b = in.nextInt() - 1;
            map[a].add(b);
            map[b].add(a);
        }
        int[]cur = new int[n];
        LinkedList<Integer> q = new LinkedList();
        for(int i = 0; i < n; i++){
            if(map[i].size() > 1){
                q.add(i);
            }
        }
        while(!q.isEmpty()){
            int node = q.pollFirst();
            int c =  0;
            for(int j : map[node]){
                if(cur[j] == cur[node]){
                    c++;
                }
            }
            if(c > 1){
                cur[node] = 1 - cur[node];
                for(int j : map[node]){
                    if(cur[j] == cur[node]){
                        int x = 0;
                        for(int k : map[j]){
                            if(cur[k] == cur[j]){
                                x++;
                            }
                        }
                        if(x > 1){
                            q.add(j);
                        }
                    }
                }
            }
        }
        for(int i : cur){
            out.print(i);
        }
        out.close();
    }

    public static int[] KMP(String val) {
        int i = 0;
        int j = -1;
        int[] result = new int[val.length() + 1];
        result[0] = -1;
        while (i < val.length()) {
            while (j >= 0 && val.charAt(j) != val.charAt(i)) {
                j = result[j];
            }
            j++;
            i++;
            result[i] = j;
        }
        return result;

    }

    public static boolean nextPer(int[] data) {
        int i = data.length - 1;
        while (i > 0 && data[i] < data[i - 1]) {
            i--;
        }
        if (i == 0) {
            return false;
        }
        int j = data.length - 1;
        while (data[j] < data[i - 1]) {
            j--;
        }
        int temp = data[i - 1];
        data[i - 1] = data[j];
        data[j] = temp;
        Arrays.sort(data, i, data.length);
        return true;
    }

    public static int digit(long n) {
        int result = 0;
        while (n > 0) {
            n /= 10;
            result++;
        }
        return result;
    }

    public static double dist(long a, long b, long x, long y) {
        double val = (b - a) * (b - a) + (x - y) * (x - y);
        val = Math.sqrt(val);
        double other = x * x + a * a;
        other = Math.sqrt(other);
        return val + other;

    }

    public static class Point implements Comparable<Point> {

        int x, y;

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

        @Override
        public int hashCode() {
            int hash = 5;
            hash = 47 * hash + this.x;
            hash = 47 * hash + this.y;
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final Point other = (Point) obj;
            if (this.x != other.x) {
                return false;
            }
            if (this.y != other.y) {
                return false;
            }
            return true;
        }

        @Override
        public int compareTo(Point o) {
            return x - o.x;
        }
    }

    public static class FT {

        long[] data;

        FT(int n) {
            data = new long[n];
        }

        public void update(int index, long value) {
            while (index < data.length) {
                data[index] += value;
                index += (index & (-index));
            }
        }

        public long get(int index) {
            long result = 0;
            while (index > 0) {
                result += data[index];
                index -= (index & (-index));
            }
            return result;

        }
    }

    public static long gcd(long a, long b) {
        if (b == 0) {
            return a;
        }
        return gcd(b, a % b);
    }

    public static long pow(long a, long b, long MOD) {
        if (b == 0) {
            return 1;
        }
        if (b == 1) {
            return a;
        }
        long val = pow(a, b / 2, MOD);
        if (b % 2 == 0) {
            return val * val % MOD;
        } else {
            return val * (val * a % MOD) % MOD;

        }
    }

    static class Scanner {

        BufferedReader br;
        StringTokenizer st;

        public Scanner() throws FileNotFoundException {
            // System.setOut(new PrintStream(new BufferedOutputStream(System.out), true));
            br = new BufferedReader(new InputStreamReader(System.in));
            //  br = new BufferedReader(new InputStreamReader(new FileInputStream(new File("input.txt"))));
        }

        public String next() {

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

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

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

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

        public String nextLine() {
            st = null;
            try {
                return br.readLine();
            } catch (Exception e) {
                throw new RuntimeException();
            }
        }

        public boolean endLine() {
            try {
                String next = br.readLine();
                while (next != null && next.trim().isEmpty()) {
                    next = br.readLine();
                }
                if (next == null) {
                    return true;
                }
                st = new StringTokenizer(next);
                return st.hasMoreTokens();
            } catch (Exception e) {
                throw new RuntimeException();
            }
        }
    }
}


import java.math.BigInteger;
import java.util.Scanner;

public class Fraction {

    BigInteger a = new BigInteger("0");
    BigInteger b = new BigInteger("1");

    Fraction() {

    }

    Fraction(long a, long b) {
        this.a = new BigInteger("" + a);
        this.b = new BigInteger("" + b);
    }

    Fraction(BigInteger a, BigInteger b) {
        this.a = a;
        this.b = b;
    }

    Fraction add(BigInteger x) {
        Fraction ans = new Fraction(a, b);
        ans.a = ans.a.add(x.multiply(b));
        ans.reduce();
        return ans;
    }

    Fraction multiply(BigInteger x) {
        Fraction ans = new Fraction(this.a, this.b);
        ans.a = ans.a.multiply(x);
        ans.reduce();
        return ans;
    }

    Fraction add(long x) {
        BigInteger bx = new BigInteger("" + x);
        Fraction ans = add(bx);
        ans.reduce();
        return ans;
    }

    Fraction multiply(long x) {
        BigInteger bx = new BigInteger("" + x);
        Fraction ans = multiply(bx);
        ans.reduce();
        return ans;
    }

    Fraction add(Fraction x) {
        BigInteger lo = b.multiply(x.b);
        BigInteger h1 = a.multiply(x.b);
        BigInteger h2 = x.a.multiply(b);
        BigInteger hi = h1.add(h2);
        Fraction ans = new Fraction(hi, lo);
        ans.reduce();
        return ans;
    }

    Fraction multiply(Fraction x) {
        BigInteger lo = b.multiply(x.b);
        BigInteger hi = a.multiply(x.a);
        Fraction ans = new Fraction(hi, lo);
        ans.reduce();
        return ans;
    }

    Fraction devide(Fraction x) {
        Fraction y = new Fraction(x.b, x.a);
        Fraction ans = multiply(y);
        ans.reduce();
        return ans;
    }

    BigInteger GCD(BigInteger a, BigInteger b) {
        for (;;) {
            if (a.equals(BigInteger.ZERO)) {
                return b;
            }
            b = b.mod(a);
            if (b.equals(BigInteger.ZERO)) {
                return a;
            }
            a = a.mod(b);
        }
    }

    void reduce() {
        BigInteger G = GCD(a, b);
        a = a.divide(G);
        b = b.divide(G);
    }
}

public class Main {

    static int n;
    static Fraction[] memo = new Fraction[100100];
    static boolean[] vis = new boolean[100100];

    static Fraction DP(int root, long depth) {
        int n2 = 0;
        while (n % 2 == 0) {
            n >>= 1;
            ++n2;
        }
        Fraction A = new Fraction(0, 1);
        Fraction B = new Fraction(1, 1);
        int lastRem = 1;
        while (root != 0) {
            int k;
            for (k = 1; (1 << k) < n; k++);
            int rem = (1 << k) - n;
            A = A.add(B.multiply(k));
            B = B.multiply(rem);
            B.b = B.b.shiftLeft(k).multiply(BigInteger.valueOf(lastRem));
            if (rem == 1) {
                B.a = B.b.subtract(B.a);
                A = A.devide(B);
                break;
            }
            lastRem = rem;
        }
        A.a = A.a.add(A.b.multiply(BigInteger.valueOf(n2)));
        return A;
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        if (n == 0) {
            System.out.println("0/1");
            return;
        }
        Fraction ans = DP(1, 0);
        ans.reduce();
        System.out.println(ans.a + "/" + ans.b);
    }
}


import java.io.*;

public class Solution
{
    public static class Frac
    {
        BigInteger numer, denom;
        Frac () { numer = BigInteger.ZERO; denom = BigInteger.ONE; }
        Frac (BigInteger num, BigInteger den)
        {
            numer = num;
            denom = den;
            BigInteger g = numer.gcd(denom);
            numer = numer.divide(g);
            denom = denom.divide(g);
        }
    }

    static int n, sza;
    static int idx[], a[];
    static Frac k[], b[], d[];
    static boolean calced[];

    public static Frac multFrac (Frac a, Frac b)
    {
        Frac ans = new Frac(a.numer.multiply(b.numer), a.denom.multiply(b.denom));
        BigInteger g = ans.numer.gcd(ans.denom);
        ans.numer = ans.numer.divide(g);
        ans.denom = ans.denom.divide(g);
        return ans;
    }

    public static Frac addFrac (Frac a, Frac b)
    {
        Frac ans = new Frac(a.numer.multiply(b.denom).add(b.numer.multiply(a.denom)), a.denom.multiply(b.denom));
        BigInteger g = ans.numer.gcd(ans.denom);
        ans.numer = ans.numer.divide(g);
        ans.denom = ans.denom.divide(g);
        return ans;
    }

    static Frac solve ()
    {
        int cnt = 1;
        int fcalc = -1;

        while (true)
        {
            a[sza] = cnt;
            idx[cnt] = sza;

            int h = 0;
            while ((cnt << h) < n)
                h++;

            int rem = (cnt << h) - n;

            if (rem == 0)
            {
                d[sza] = new Frac(BigInteger.valueOf(h), BigInteger.ONE);
                fcalc = sza;
                break;
            }

            b[sza] = new Frac(BigInteger.valueOf(h), BigInteger.ONE);
            k[sza] = new Frac(BigInteger.valueOf(rem), BigInteger.valueOf(cnt << h));

            if (idx[rem] != -1)
            {
                Frac curb = b[sza], curk = k[sza];

                for (int i = sza - 1; i >= idx[rem]; i--)
                {
                    Frac nb = addFrac(b[i], multFrac(curb, k[i]));
                    Frac nk = multFrac(curk, k[i]);
                    curb = nb; curk = nk;
                }

                d[idx[rem]] = multFrac(new Frac(curk.denom, curk.denom.subtract(curk.numer)), curb);

                fcalc = idx[rem];
                break;
            }
            sza++;
            cnt = rem;
        }

        Frac cur = d[fcalc];
        for (int i = fcalc - 1; i >= 0; i--)
            cur = addFrac(b[i], multFrac(k[i], cur));

        return cur;
    }

    public static void main (String[] argv)
    {
        Scanner in = new Scanner (System.in);
        n = in.nextInt();
        idx = new int [n + 5];
        for (int i = 0; i <= n; i++)
            idx[i] = -1;
        a = new int [n + 5];
        sza = 0;
        k = new Frac [n + 5];
        b = new Frac [n + 5];
        d = new Frac [n + 5];

        Frac ans = solve();

        System.out.println(ans.numer + "/" + ans.denom);
    }
}


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

public class Main {
	public static int []to = new int[10];
	public static void main(String args[]) {
		Scanner in = new Scanner(System.in);
		int n = in.nextInt(), start = -1;
		if (n == 1) {
			System.out.println("0/1");
			return;
		}
		for (int i=0,rem=1,nxt_rem=-1; ; rem=nxt_rem,++i) {
			nxt_rem = rem * 2;
			if (nxt_rem >= n) nxt_rem -= n;
			if (to[nxt_rem] != 0) {
				start = nxt_rem;
				break;
			}
			to[rem] = nxt_rem;
			if (rem == 0) {
				System.out.println(i+"/1");
				return;
			}
		}
		Fraction A = new Fraction(BigInteger.ONE, BigInteger.ONE);
		int next_start = 2 * start;
		if (next_start >= n) next_start -= n;
		Fraction B = new Fraction(BigInteger.valueOf(next_start), BigInteger.valueOf(2*start));
		for (int i=next_start; ; ) {
			int b = i * 2;
			int a = b;
			if (a >= n) a -= n;
			Fraction C = new Fraction(BigInteger.valueOf(a), BigInteger.valueOf(b));
			A = A.add(B);
			B = B.multiply(C);
			i = a;
			if (a == start) {
				B.a = B.b.subtract(B.a);
				A = A.divide(B);
				break;
			}
		}
		for (int i=1; i!=start; ) {
			A.a = A.a.add(A.b);
			int j = i * 2;
			if (j >= n) j -= n;
			i = j;
		}
		System.out.println(A.a+"/"+A.b);
	}
}

class Fraction {
	public BigInteger a, b;
	public Fraction(BigInteger a, BigInteger b) {
		BigInteger d = a.gcd(b);
		this.a = a.divide(d);
		this.b = b.divide(d);
	}
	public Fraction add(Fraction rhs) {
		BigInteger x = this.a.multiply(rhs.b).add(this.b.multiply(rhs.a));
		BigInteger y = this.b.multiply(rhs.b);
		BigInteger d = x.gcd(y);
		x = x.divide(d);
		y = y.divide(d);
		return new Fraction(x, y);
	}
	public Fraction multiply(Fraction rhs) {
		BigInteger x = this.a.multiply(rhs.a);
		BigInteger y = this.b.multiply(rhs.b);
		BigInteger d = x.gcd(y);
		x = x.divide(d);
		y = y.divide(d);
		return new Fraction(x, y);
	}
	public Fraction divide(Fraction rhs) {
		Fraction r = new Fraction(rhs.b, rhs.a);
		return this.multiply(r);
	}
}


import java.io.PrintWriter;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Locale;
import java.util.Random;
import java.util.Scanner;

import org.omg.CORBA.FREE_MEM;

public class Main {
    class Pair{
        Long l1;
        Long l2;
    };
    HashMap<Pair, Fraction> F = new HashMap<Pair, Fraction>();

    class Fraction{
        public BigInteger p, q;

        public Fraction() {
            p = BigInteger.ZERO;
            q = BigInteger.ONE;
        }

        Fraction multiply(long v){
            Fraction ret = new Fraction();
            ret.p = p.multiply(BigInteger.valueOf(v));
            ret.q = q;
            ret.fix();
            return ret;
        }
        Fraction divide(long v){
            Fraction ret = new Fraction();
            ret.p = p;
            ret.q = q.multiply(BigInteger.valueOf(v));
            ret.fix();
            return ret;
        }
        Fraction divide(Fraction v){
            Fraction ret = new Fraction();
            ret.p = p.multiply(v.q);
            ret.q = q.multiply(v.p);
            ret.fix();
            return ret;
        }
        boolean less(Fraction f2){
            return p.multiply(f2.q).compareTo(f2.p.multiply(q)) < 0;
        }
        Fraction add(Fraction f2){
            Fraction ret = new Fraction();
            ret.p = p.multiply(f2.q).add(f2.p.multiply(q));
            ret.q = q.multiply(f2.q);
            return ret;
        }
        Fraction sub(Fraction f2){
            Fraction ret = new Fraction();
            ret.p = p.multiply(f2.q).subtract(f2.p.multiply(q));
            ret.q = q.multiply(f2.q);
            return ret;
        }

        void fix(){
            BigInteger gc = p.gcd(q);
            p = p.divide(gc);
            q = q.divide(gc);
        }

        public String toString(){
            return p.toString() + "/" + q.toString();
        }
    }
    void solve(){
        Scanner in = new Scanner(System.in);
        PrintWriter out = new PrintWriter(System.out);
        int n = in.nextInt();
        if (n == 1){
            out.println("0/1");
            out.flush();
            return;
        }

        Fraction[] was = new Fraction[n+1];
        Fraction[] pp = new Fraction[n+1];
        Fraction ret = new Fraction();
        Fraction p = new Fraction();
        p.p = BigInteger.ONE;
        int fre = 1;
        was[1] = new Fraction();
        pp[1] = p;
        while (true){
            fre *= 2;
            ret = ret.add(p);
            if (fre >= n){
                p = p.multiply(fre - n);
                p = p.divide(fre);
                fre -= n;
            }
            if (was[fre] == null){
                was[fre] = ret;
                pp[fre] = p;
            } else {
                Fraction f = ret.sub(was[fre]);
                f = f.divide(pp[fre].sub(p));
                if (f.p.compareTo(BigInteger.ZERO) != 0){
                    ret = f.add(was[fre]);
                }
                break;
            }
        }
        ret.fix();
        out.println(ret);
        out.flush();
    }
    public static void main(String[] args) {
        Main solver = new Main();
        solver.solve();
    }

}


// https://codeforces.com/contest/1174/submission/55043857 (rainboy)
import java.io.*;

public class CF1174E {
	static final int MD = 1000000007;
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int n = Integer.parseInt(br.readLine());
		int[] aa = new int[n + 1];
		for (int i = 1; i <= n; i++)
			aa[i] = i % 2 == 0 ? aa[i / 2] + 1 : 0;
		int k = 0;
		while (1 << k + 1 <= n)
			k++;
		int[] cnt = new int[k + 1];
		int[] cnt3 = new int[k + 1];
		for (int i = 1; i <= n; i++) {
			int a = aa[i];
			cnt[a]++;
			if (i % 3 == 0)
				cnt3[a]++;
		}
		long[][] dp = new long[n + 1][k + 1];
		dp[1][k] = 1;
		for (int i = 1; i < n; i++)
			for (int h = 0; h <= k; h++) {
				long x = dp[i][h];
				if (x != 0) {
					if (h > 0)
						dp[i + 1][h - 1] = (dp[i + 1][h - 1] + x * cnt[h - 1]) % MD;
					dp[i + 1][h] = (dp[i + 1][h] + x * (n / (1 << h) - i)) % MD;
				}
			}
		long ans = dp[n][0];
		if (3 << k - 1 <= n) {
			dp = new long[n + 1][k];
			long[][] dq = new long[n + 1][k];
			dq[1][k - 1] = 1;
			for (int i = 1; i < n; i++)
				for (int h = 0; h < k; h++) {
					long x = dq[i][h];
					if (x != 0) {
						if (h > 0)
							dq[i + 1][h - 1] = (dq[i + 1][h - 1] + x * cnt3[h - 1]) % MD;
						dq[i + 1][h] = (dq[i + 1][h] + x * (n / (3 << h) - i)) % MD;
						dp[i + 1][h] = (dp[i + 1][h] + x * (n / (1 << h) - i)) % MD;
					}
					x = dp[i][h];
					if (x != 0) {
						if (h > 0)
							dp[i + 1][h - 1] = (dp[i + 1][h - 1] + x * cnt[h - 1]) % MD;
						dp[i + 1][h] = (dp[i + 1][h] + x * (n / (1 << h) - i)) % MD;
					}
				}
			ans = (ans + dp[n][0]) % MD;
		}
		System.out.println(ans);
	}
}


/*
If you want to aim high, aim high
Don't let that studying and grades consume you
Just live life young
******************************
What do you think? What do you think?
1st on Billboard, what do you think of it
Next is a Grammy, what do you think of it
However you think, I’m sorry, but shit, I have no fcking interest
*******************************
I'm standing on top of my Monopoly board
That means I'm on top of my game and it don't stop
til my hip don't hop anymore
https://www.a2oj.com/Ladder16.html
*******************************
300iq as writer = Sad!
*/
import java.util.*;
import java.io.*;
import java.math.*;

   public class x1174E
   {
      static long MOD = 1000000007L;
      public static void main(String hi[]) throws Exception
      {
         BufferedReader infile = new BufferedReader(new InputStreamReader(System.in));
         StringTokenizer st = new StringTokenizer(infile.readLine());
         int N = Integer.parseInt(st.nextToken());
         long[][] dp = new long[20][2];
         base(dp, N);
         cnt = new long[20][2];
         for(int curr=0; (1<<curr) <= N; curr++)
            for(int b=1; b <= 3; b+=2)
            {
               int temp = (1<<curr)*b;
               cnt[curr][Math.max(b-2, 0)] = N/temp;
            }
         //iterative sad
         for(int i=2; i <= N; i++)
         {
            long[][] newdp = new long[20][2];
            for(int a=19; a >= 0; a--)
               for(int b=1; b >= 0; b--)
               {
                  long res = 0L;
                  if(cnt[a][b]-i >= 0)
                     res = (dp[a][b]*(cnt[a][b]-i+1+MOD))%MOD;
                  if(a+1 < 20)
                  {
                     res += (dp[a+1][b]*(cnt[a][b]-cnt[a+1][b]))%MOD;
                     if(res >= MOD)
                        res -= MOD;
                  }
                  if(b == 0)
                  {
                     res += (dp[a][1]*(cnt[a][0]-cnt[a][1]))%MOD;
                     if(res >= MOD)
                        res -= MOD;
                  }
                  newdp[a][b] = res;
               }
            dp = newdp;
         }
         System.out.println(dp[0][0]);
      }
      static long[][] cnt;
      public static void base(long[][] dp, int N)
      {
         int curr = 1;
         while((1<<curr) <= N)
            curr++;
         curr--;
         dp[curr][0] = 1L;
         int oth = 1<<(curr-1);
         oth *= 3;
         if(oth <= N)
            dp[curr-1][1] = 1L;
      }
   }


import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class CF1174E {
    public static void main(String[] args) throws Exception {
        boolean local = System.getProperty("ONLINE_JUDGE") == null;
        boolean async = false;

        Charset charset = Charset.forName("ascii");

        FastIO io = local ? new FastIO(new FileInputStream("D:\\DATABASE\\TESTCASE\\Code.in"), System.out, charset) : new FastIO(System.in, System.out, charset);
        Task task = new Task(io, new Debug(local));

        if (async) {
            Thread t = new Thread(null, task, "dalt", 1 << 27);
            t.setPriority(Thread.MAX_PRIORITY);
            t.start();
            t.join();
        } else {
            task.run();
        }

        if (local) {
            io.cache.append("\n\n--memory -- \n" + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) >> 20) + "M");
        }

        io.flush();
    }

    public static class Task implements Runnable {
        final FastIO io;
        final Debug debug;
        int inf = (int) 1e8;
        MathUtils.Modular modular = new MathUtils.Modular((int) 1e9 + 7);
        MathUtils.Factorial factorial = new MathUtils.Factorial(1000000, modular);
        MathUtils.Composite composite = new MathUtils.Composite(factorial, modular);
        MathUtils.Power power = new MathUtils.Power(modular);

        public Task(FastIO io, Debug debug) {
            this.io = io;
            this.debug = debug;
        }

        @Override
        public void run() {
            solve();
        }

        int n;

        public void solve() {
            n = io.readInt();
            int time = 0;
            int base2 = 1;
            while (base2 * 2 <= n) {
                base2 *= 2;
                time++;
            }
            int base3 = 3 * (1 << (time - 1));

            int total = f(time, 0);
            if (base3 <= n) {
                total = modular.plus(total, g(time - 1));
            }
            io.cache.append(total);
        }

        int f(int i, int used) {
            if (i < 0) {
                return 1;
            }
            int sum = n / (1 << i);
            int count = sum - used;
            int result = 1;
            result = modular.mul(result, count);
            result = modular.mul(result, f(i - 1, sum));
            result = modular.mul(result, composite.composite(n - sum + count - 1, count - 1));
            result = modular.mul(result, factorial.fact[count - 1]);
            return result;
        }

        int g(int i) {
            if (i < 0) {
                return 0;
            }
            int sum = n / 3 / (1 << i);
            int count = sum - sum / 2;
            int result = 1;
            result = modular.mul(result, count);
            result = modular.mul(result, g(i - 1) + f(i, sum));
            result = modular.mul(result, composite.composite(n - sum + count - 1, count - 1));
            result = modular.mul(result, factorial.fact[count - 1]);
            return result;
        }
    }

    public static class MathUtils {
        private static Random random = new Random(123456789);

        public static class ExtLucasFactorial {
            int exp;
            int fact;
            int p;
            int pc;
            Modular modular;
            Power power;
            ExtGCD extGCD = new ExtGCD();
            int[] g;

            /**
             * O(pc)
             *
             * @param p  the prime
             * @param pc p^c
             * @param g  buffer
             */
            public ExtLucasFactorial(int p, int pc, int[] g) {
                this.p = p;
                this.pc = pc;
                this.g = g;
                modular = new Modular(pc);
                power = new Power(modular);
                g[0] = 1;
                g[1] = 1;
                for (int i = 2; i <= pc; i++) {
                    if (i % p == 0) {
                        g[i] = g[i - 1];
                    } else {
                        g[i] = modular.mul(g[i - 1], i);
                    }
                }
            }

            /**
             * return m! (mod pc) without any factor which is multiple of p.
             *

             * O(\log_2^2{m})
             */
            private int fact(long m) {
                fact = 1;
                exp = 0;
                while (m > 1) {
                    exp += m / p;
                    fact = modular.mul(fact, power.pow(g[pc], m / pc));
                    fact = modular.mul(fact, g[(int) (m % pc)]);
                    m /= p;
                }
                return fact;
            }

            /**
             * Find C(m,n), it means choose n elements from a set whose size is m.
             *

             * O(\log_2^2{m})
             */
            public int composite(long m, long n) {
                int v = fact(m);
                int e = exp;
                extGCD.extgcd(fact(n), pc);
                v = modular.mul(v, modular.valueOf(extGCD.getX()));
                e -= exp;
                extGCD.extgcd(fact(m - n), pc);
                v = modular.mul(v, modular.valueOf(extGCD.getX()));
                e -= exp;
                v = modular.mul(v, power.pow(p, e));
                return v;
            }
        }

        /**
         * 扩展卢卡斯算法
         */
        public static class ExtLucas {
            PollardRho pr = new PollardRho();
            Map<Integer, ExtLucasFactorial> factorialMap = new HashMap();

            public ExtLucas(int p) {
                Map<Integer, Integer> factors = pr.findAllFactors(p);
                for (Map.Entry<Integer, Integer> entry : factors.entrySet()) {
                    factorialMap.put(entry.getValue(), new ExtLucasFactorial(entry.getKey(), entry.getValue(), new int[entry.getValue() + 1]));
                }
            }

            /**
             * Get C(m, n) % p
             */
            public int composite(long m, long n) {
                ExtCRT extCRT = new ExtCRT();
                for (Map.Entry<Integer, ExtLucasFactorial> entry : factorialMap.entrySet()) {
                    extCRT.add(entry.getValue().composite(m, n), entry.getKey());
                }
                return (int) extCRT.r;
            }
        }

        /**
         * 扩展卢卡斯算法
         */
        public static class LongExtLucas {
            LongPollardRho pr = new LongPollardRho();
            Map<Integer, ExtLucasFactorial> factorialMap = new HashMap();

            public LongExtLucas(long p) {
                Map<Long, Long> factors = pr.findAllFactors(p);
                for (Map.Entry<Long, Long> entry : factors.entrySet()) {
                    factorialMap.put(entry.getValue().intValue(), new ExtLucasFactorial(entry.getKey().intValue(), entry.getValue().intValue(), new int[entry.getValue().intValue() + 1]));
                }
            }

            /**
             * Get C(m, n) % p
             */
            public int composite(long m, long n) {
                ExtCRT extCRT = new ExtCRT();
                for (Map.Entry<Integer, ExtLucasFactorial> entry : factorialMap.entrySet()) {
                    extCRT.add(entry.getValue().composite(m, n), entry.getKey());
                }
                return (int) extCRT.r;
            }
        }

        /**
         * 扩展欧几里得
         */
        public static class ExtGCD extends Gcd {
            private long x;
            private long y;

            public long getX() {
                return x;
            }

            public long getY() {
                return y;
            }

            /**
             * Get g = Gcd(a, b) and find a way to set x and y to match ax+by=g
             */
            public long extgcd(long a, long b) {
                if (a >= b) {
                    return extgcd0(a, b);
                } else {
                    long g = extgcd0(b, a);
                    long tmp = x;
                    x = y;
                    y = tmp;
                    return g;
                }
            }

            private long extgcd0(long a, long b) {
                if (b == 0) {
                    x = 1;
                    y = 0;
                    return a;
                }
                long g = extgcd0(b, a % b);
                long n = x;
                long m = y;
                x = m;
                y = n - m * (a / b);
                return g;
            }
        }

        public static class Gcd {
            public long gcd(long a, long b) {
                return a >= b ? gcd0(a, b) : gcd0(b, a);
            }

            private long gcd0(long a, long b) {
                return b == 0 ? a : gcd0(b, a % b);
            }

            public int gcd(int a, int b) {
                return a >= b ? gcd0(a, b) : gcd0(b, a);
            }

            private int gcd0(int a, int b) {
                return b == 0 ? a : gcd0(b, a % b);
            }
        }

        /**
         * 欧拉筛
         */
        public static class EulerSieve {
            int[] primes;
            boolean[] isComp;
            int primeLength;

            public EulerSieve(int limit) {
                isComp = new boolean[limit + 1];
                primes = new int[limit + 1];
                primeLength = 0;
                for (int i = 2; i <= limit; i++) {
                    if (!isComp[i]) {
                        primes[primeLength++] = i;
                    }
                    for (int j = 0, until = limit / i; j < primeLength && primes[j] <= until; j++) {
                        int pi = primes[j] * i;
                        isComp[pi] = true;
                        if (i % primes[j] == 0) {
                            break;
                        }
                    }
                }
            }
        }

        /**
         * 模运算
         */
        public static class Modular {
            final int m;

            public Modular(int m) {
                this.m = m;
            }

            public int valueOf(int x) {
                x %= m;
                if (x < 0) {
                    x += m;
                }
                return x;
            }

            public int valueOf(long x) {
                x %= m;
                if (x < 0) {
                    x += m;
                }
                return (int) x;
            }

            public int mul(int x, int y) {
                return valueOf((long) x * y);
            }

            public int plus(int x, int y) {
                return valueOf(x + y);
            }

            @Override
            public String toString() {
                return "mod " + m;
            }
        }

        /**
         * 位运算
         */
        public static class BitOperator {
            public int bitAt(int x, int i) {
                return (x >> i) & 1;
            }

            public int bitAt(long x, int i) {
                return (int) ((x >> i) & 1);
            }

            public int setBit(int x, int i, boolean v) {
                if (v) {
                    x |= 1 << i;
                } else {
                    x &= ~(1 << i);
                }
                return x;
            }

            public long setBit(long x, int i, boolean v) {
                if (v) {
                    x |= 1L << i;
                } else {
                    x &= ~(1L <> 1);
                r = modular.valueOf(r * r);
                if ((n & 1) == 1) {
                    r = modular.valueOf(r * x);
                }
                return (int) r;
            }

            public int inverse(int x) {
                return pow(x, modular.m - 2);
            }

            public int pow2(int x) {
                return x * x;
            }

            public long pow2(long x) {
                return x * x;
            }

            public double pow2(double x) {
                return x * x;
            }
        }

        /**
         * 对数
         */
        public static class Log2 {
            public int ceilLog(int x) {
                return 32 - Integer.numberOfLeadingZeros(x - 1);
            }

            public int floorLog(int x) {
                return 31 - Integer.numberOfLeadingZeros(x);
            }

            public int ceilLog(long x) {
                return 64 - Long.numberOfLeadingZeros(x - 1);
            }

            public int floorLog(long x) {
                return 63 - Long.numberOfLeadingZeros(x);
            }
        }

        /**
         * 乘法逆元
         */
        public static class InverseNumber {
            int[] inv;

            public InverseNumber(int[] inv, int limit, Modular modular) {
                this.inv = inv;
                inv[1] = 1;
                int p = modular.m;
                for (int i = 2; i <= limit; i++) {
                    int k = p / i;
                    int r = p % i;
                    inv[i] = modular.mul(-k, inv[r]);
                }
            }

            public InverseNumber(int limit, Modular modular) {
                this(new int[limit + 1], limit, modular);
            }
        }

        /**
         * 排列
         */
        public static class Factorial {
            int[] fact;
            int[] inv;

            public Factorial(int[] fact, int[] inv, InverseNumber in, int limit, Modular modular) {
                this.fact = fact;
                this.inv = inv;
                fact[0] = inv[0] = 1;
                for (int i = 1; i <= limit; i++) {
                    fact[i] = modular.mul(fact[i - 1], i);
                    inv[i] = modular.mul(inv[i - 1], in.inv[i]);
                }
            }

            public Factorial(int limit, Modular modular) {
                this(new int[limit + 1], new int[limit + 1], new InverseNumber(limit, modular), limit, modular);
            }
        }

        /**
         * 组合
         */
        public static class Composite {
            final Factorial factorial;
            final Modular modular;

            public Composite(Factorial factorial, Modular modular) {
                this.factorial = factorial;
                this.modular = modular;
            }

            public Composite(int limit, Modular modular) {
                this(new Factorial(limit, modular), modular);
            }

            public int composite(int m, int n) {
                if (n > m) {
                    return 0;
                }
                return modular.mul(modular.mul(factorial.fact[m], factorial.inv[n]), factorial.inv[m - n]);
            }
        }

        /**
         * 大素数测试
         */
        public static class MillerRabin {
            Modular modular;
            Power power;

            /**
             * 判断n是否是素数
             */
            public boolean mr(int n, int s) {
                if (n == 2) {
                    return true;
                }
                if (n % 2 == 0) {
                    return false;
                }
                modular = new Modular(n);
                power = new Power(modular);
                for (int i = 0; i < s; i++) {
                    int x = random.nextInt(n - 2) + 2;
                    if (!mr0(x, n)) {
                        return false;
                    }
                }
                return true;
            }

            private boolean mr0(int x, int n) {
                int exp = n - 1;
                while (true) {
                    int y = power.pow(x, exp);
                    if (y != 1 && y != n - 1) {
                        return false;
                    }
                    if (y != 1 || exp % 2 == 1) {
                        break;
                    }
                    exp = exp / 2;
                }
                return true;
            }
        }

        public static class LongModular {
            final long m;

            public LongModular(long m) {
                this.m = m;
            }

            public long mul(long a, long b) {
                return b == 0 ? 0 : ((mul(a, b >> 1) << 1) % m + a * (b & 1)) % m;
            }

            public long plus(long a, long b) {
                return valueOf(a + b);
            }

            public long valueOf(long a) {
                a %= m;
                if (a < 0) {
                    a += m;
                }
                return a;
            }
        }

        public static class LongPower {
            final LongModular modular;

            public LongPower(LongModular modular) {
                this.modular = modular;
            }

            long pow(long x, long n) {
                if (n == 0) {
                    return 1;
                }
                long r = pow(x, n >> 1);
                r = modular.mul(r, r);
                if ((n & 1) == 1) {
                    r = modular.mul(r, x);
                }
                return r;
            }

            long inverse(long x) {
                return pow(x, modular.m - 2);
            }
        }

        /**
         * 大素数测试
         */
        public static class LongMillerRabin {
            LongModular modular;
            LongPower power;

            /**
             * 判断n是否是素数
             */
            public boolean mr(long n, int s) {
                if (n == 2) {
                    return true;
                }
                if (n % 2 == 0) {
                    return false;
                }
                modular = new LongModular(n);
                power = new LongPower(modular);
                for (int i = 0; i < s; i++) {
                    long x = (long) (random.nextDouble() * (n - 2) + 2);
                    if (!mr0(x, n)) {
                        return false;
                    }
                }
                return true;
            }

            private boolean mr0(long x, long n) {
                long exp = n - 1;
                while (true) {
                    long y = power.pow(x, exp);
                    if (y != 1 && y != n - 1) {
                        return false;
                    }
                    if (y != 1 || exp % 2 == 1) {
                        break;
                    }
                    exp = exp / 2;
                }
                return true;
            }
        }

        public static class LongPollardRho {
            LongMillerRabin mr = new LongMillerRabin();
            Gcd gcd = new Gcd();
            LongModular modular;

            /**
             * Find a factor of n, if n is returned, it means n is 1 or a prime
             */
            public long findFactor(long n) {
                if (mr.mr(n, 3)) {
                    return n;
                }
                modular = new LongModular(n);
                while (true) {
                    long f = findFactor0((long) (random.nextDouble() * n), (long) (random.nextDouble() * n), n);
                    if (f != -1) {
                        return f;
                    }
                }
            }

            private long findFactor0(long x, long c, long n) {
                long xi = x;
                long xj = x;
                int j = 2;
                int i = 1;
                while (i < n) {
                    i++;
                    xi = modular.plus(modular.mul(xi, xi), c);
                    long g = gcd.gcd(n, Math.abs(xi - xj));
                    if (g != 1 && g != n) {
                        return g;
                    }
                    if (i == j) {
                        j = j << 1;
                        xj = xi;
                    }
                }
                return -1;
            }

            /**
             * Find the representation of n=p1^c1 * p2^c2 * ... * pm ^ cm.
             *

             * The returned map contained such entries: pi -> pi^ci
             */
            public Map<Long, Long> findAllFactors(long n) {
                Map<Long, Long> map = new HashMap();
                findAllFactors(map, n);
                return map;
            }

            private void findAllFactors(Map<Long, Long> map, long n) {
                if (n == 1) {
                    return;
                }
                long f = findFactor(n);
                if (f == n) {
                    Long value = map.get(f);
                    if (value == null) {
                        value = 1L;
                    }
                    map.put(f, value * f);
                    return;
                }
                findAllFactors(map, f);
                findAllFactors(map, n / f);
            }

        }

        /**
         * 扩展中国余数定理
         */
        public static class ExtCRT {
            /**
             * remainder
             */
            long r;
            /**
             * modulus
             */
            long m;
            ExtGCD gcd = new ExtGCD();

            public ExtCRT() {
                r = 0;
                m = 1;
            }

            /**
             * Add a new condition: x % m = r
             */
            public boolean add(long r, long m) {
                long m1 = this.m;
                long x1 = this.r;
                long m2 = m;
                long x2 = ((r % m) + m) % m;
                long g = gcd.extgcd(m1, m2);
                long a = gcd.getX();
                if ((x2 - x1) % g != 0) {
                    return false;
                }
                this.m = m1 / g * m2;
                this.r = BigInteger.valueOf(a).multiply(BigInteger.valueOf((x2 - x1) / g))
                        .multiply(BigInteger.valueOf(m1)).add(BigInteger.valueOf(x1))
                        .mod(BigInteger.valueOf(this.m)).longValue();
                return true;
            }
        }

        /**
         * 卢卡斯定理
         */
        public static class Lucas {
            private final Composite composite;
            private int modulus;

            public Lucas(Composite composite) {
                this.composite = composite;
                this.modulus = composite.modular.m;
            }

            public int composite(long m, long n) {
                if (n == 0) {
                    return 1;
                }
                return composite.modular.mul(composite.composite((int) (m % modulus), (int) (n % modulus)),
                        composite(m / modulus, n / modulus));
            }
        }

        /**
         * 因式分解
         */
        public static class PollardRho {
            MillerRabin mr = new MillerRabin();
            Gcd gcd = new Gcd();
            Random random = new Random(123456789);

            public int findFactor(int n) {
                if (mr.mr(n, 10)) {
                    return n;
                }
                while (true) {
                    int f = findFactor0(random.nextInt(n), random.nextInt(n), n);
                    if (f != -1) {
                        return f;
                    }
                }
            }

            public Map<Integer, Integer> findAllFactors(int n) {
                Map<Integer, Integer> map = new HashMap();
                findAllFactors(map, n);
                return map;
            }

            private void findAllFactors(Map<Integer, Integer> map, int n) {
                if (n == 1) {
                    return;
                }
                int f = findFactor(n);
                if (f == n) {
                    Integer value = map.get(f);
                    if (value == null) {
                        value = 1;
                    }
                    map.put(f, value * f);
                    return;
                }
                findAllFactors(map, f);
                findAllFactors(map, n / f);
            }

            private int findFactor0(int x, int c, int n) {
                int xi = x;
                int xj = x;
                int j = 2;
                int i = 1;
                while (i < n) {
                    i++;
                    xi = (int) ((long) xi * xi + c) % n;
                    int g = gcd.gcd(n, Math.abs(xi - xj));
                    if (g != 1 && g != n) {
                        return g;
                    }
                    if (i == j) {
                        j = j << 1;
                        xj = xi;
                    }
                }
                return -1;
            }
        }
    }

    public static class FastIO {
        public final StringBuilder cache = new StringBuilder();
        private final InputStream is;
        private final OutputStream os;
        private final Charset charset;
        private StringBuilder defaultStringBuf = new StringBuilder(1 << 8);
        private byte[] buf = new byte[1 << 13];
        private int bufLen;
        private int bufOffset;
        private int next;

        public FastIO(InputStream is, OutputStream os, Charset charset) {
            this.is = is;
            this.os = os;
            this.charset = charset;
        }

        public FastIO(InputStream is, OutputStream os) {
            this(is, os, Charset.forName("ascii"));
        }

        private int read() {
            while (bufLen == bufOffset) {
                bufOffset = 0;
                try {
                    bufLen = is.read(buf);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                if (bufLen == -1) {
                    return -1;
                }
            }
            return buf[bufOffset++];
        }

        public void skipBlank() {
            while (next >= 0 && next <= 32) {
                next = read();
            }
        }

        public int readInt() {
            int sign = 1;

            skipBlank();
            if (next == '+' || next == '-') {
                sign = next == '+' ? 1 : -1;
                next = read();
            }

            int val = 0;
            if (sign == 1) {
                while (next >= '0' && next <= '9') {
                    val = val * 10 + next - '0';
                    next = read();
                }
            } else {
                while (next >= '0' && next <= '9') {
                    val = val * 10 - next + '0';
                    next = read();
                }
            }

            return val;
        }

        public long readLong() {
            int sign = 1;

            skipBlank();
            if (next == '+' || next == '-') {
                sign = next == '+' ? 1 : -1;
                next = read();
            }

            long val = 0;
            if (sign == 1) {
                while (next >= '0' && next <= '9') {
                    val = val * 10 + next - '0';
                    next = read();
                }
            } else {
                while (next >= '0' && next <= '9') {
                    val = val * 10 - next + '0';
                    next = read();
                }
            }

            return val;
        }

        public double readDouble() {
            boolean sign = true;
            skipBlank();
            if (next == '+' || next == '-') {
                sign = next == '+';
                next = read();
            }

            long val = 0;
            while (next >= '0' && next <= '9') {
                val = val * 10 + next - '0';
                next = read();
            }
            if (next != '.') {
                return sign ? val : -val;
            }
            next = read();
            long radix = 1;
            long point = 0;
            while (next >= '0' && next <= '9') {
                point = point * 10 + next - '0';
                radix = radix * 10;
                next = read();
            }
            double result = val + (double) point / radix;
            return sign ? result : -result;
        }

        public String readString(StringBuilder builder) {
            skipBlank();

            while (next > 32) {
                builder.append((char) next);
                next = read();
            }

            return builder.toString();
        }

        public String readString() {
            defaultStringBuf.setLength(0);
            return readString(defaultStringBuf);
        }

        public int readLine(char[] data, int offset) {
            int originalOffset = offset;
            while (next != -1 && next != '\n') {
                data[offset++] = (char) next;
                next = read();
            }
            return offset - originalOffset;
        }

        public int readString(char[] data, int offset) {
            skipBlank();

            int originalOffset = offset;
            while (next > 32) {
                data[offset++] = (char) next;
                next = read();
            }

            return offset - originalOffset;
        }

        public int readString(byte[] data, int offset) {
            skipBlank();

            int originalOffset = offset;
            while (next > 32) {
                data[offset++] = (byte) next;
                next = read();
            }

            return offset - originalOffset;
        }

        public char readChar() {
            skipBlank();
            char c = (char) next;
            next = read();
            return c;
        }

        public void flush() {
            try {
                os.write(cache.toString().getBytes(charset));
                os.flush();
                cache.setLength(0);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        public boolean hasMore() {
            skipBlank();
            return next != -1;
        }
    }

    public static class Debug {
        private boolean allowDebug;

        public Debug(boolean allowDebug) {
            this.allowDebug = allowDebug;
        }

        public void assertTrue(boolean flag) {
            if (!allowDebug) {
                return;
            }
            if (!flag) {
                fail();
            }
        }

        public void fail() {
            throw new RuntimeException();
        }

        public void assertFalse(boolean flag) {
            if (!allowDebug) {
                return;
            }
            if (flag) {
                fail();
            }
        }

        private void outputName(String name) {
            System.out.print(name + " = ");
        }

        public void debug(String name, int x) {
            if (!allowDebug) {
                return;
            }

            outputName(name);
            System.out.println("" + x);
        }

        public void debug(String name, long x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println("" + x);
        }

        public void debug(String name, double x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println("" + x);
        }

        public void debug(String name, int[] x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println(Arrays.toString(x));
        }

        public void debug(String name, long[] x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println(Arrays.toString(x));
        }

        public void debug(String name, double[] x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println(Arrays.toString(x));
        }

        public void debug(String name, Object x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println("" + x);
        }

        public void debug(String name, Object... x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println(Arrays.deepToString(x));
        }
    }
}


// https://codeforces.com/contest/1174/submission/55043857 (rainboy)
import java.io.*;

public class CF1174E {
	static final int MD = 1000000007;
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int n = Integer.parseInt(br.readLine());
		int[] aa = new int[n + 1];
		for (int i = 1; i <= n; i++)
			aa[i] = i % 2 == 0 ? aa[i / 2] + 1 : 0;
		int k = 0;
		while (1 << k + 1 <= n)
			k++;
		int[] cnt = new int[k + 1];
		int[] cnt3 = new int[k + 1];
		for (int i = 1; i <= n; i++) {
			int a = aa[i];
			cnt[a]++;
			if (i % 3 == 0)
				cnt3[a]++;
		}
		long[][] dp = new long[n + 1][k + 1];
		dp[1][k] = 1;
		for (int i = 1; i < n; i++)
			for (int h = 0; h <= k; h++) {
				long x = dp[i][h];
				if (x != 0) {
					if (h > 0)
						dp[i + 1][h - 1] = (dp[i + 1][h - 1] + x * cnt[h - 1]) % MD;
					dp[i + 1][h] = (dp[i + 1][h] + x * (n / (1 << h) - i)) % MD;
				}
			}
		long ans = dp[n][0];
		if (3 << k - 1 <= n) {
			dp = new long[n + 1][k];
			long[][] dq = new long[n + 1][k];
			dq[1][k - 1] = 1;
			for (int i = 1; i < n; i++)
				for (int h = 0; h < k; h++) {
					long x = dq[i][h];
					if (x != 0) {
						if (h > 0)
							dq[i + 1][h - 1] = (dq[i + 1][h - 1] + x * cnt3[h - 1]) % MD;
						dq[i + 1][h] = (dq[i + 1][h] + x * (n / (3 << h) - i)) % MD;
						dp[i + 1][h] = (dp[i + 1][h] + x * (n / (1 << h) - n / (3 << h))) % MD;
					}
					x = dp[i][h];
					if (x != 0) {
						if (h > 0)
							dp[i + 1][h - 1] = (dp[i + 1][h - 1] + x * cnt[h - 1]) % MD;
						dp[i + 1][h] = (dp[i + 1][h] + x * (n / (1 << h) - i)) % MD;
					}
				}
			ans = (ans + dp[n][0]) % MD;
		}
		System.out.println(ans);
	}
}


import java.io.*;
public class Coloring
{
    public static void main(String...args)
    {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = br.readLine();
        String[] st = s.trim().split("\\s+");
        int n = Integer.parseInt(st[0]);
        int m = Integer.parseInt(st[1]);

        if(n < m)
            swap(n , m);

        long mod = 998244353;

        long res[][] = new long[n+m][n];

        for(int i = 0 ; i < n+m ; i++){
            for(int j = 0 ; j < n ; j++){
                if(j == 0)
                    res[i][j] = 1;
                else if(i == 0)
                    res[i][j] = 0;
                else
                    res[i][j] = res[i-1][j-1] + res[i-1][j];
            }
        }

        long ans2 = 0;

        for(int rows = 1 ; rows < n ; rows++){
            long ans1 = 0;
            for(int col = m-1 ; col >=0 ; col--){
                ans1 = (ans1 + res[n - rows + m - col -1][n - rows]*(res[n - rows + col - 1][n - rows - 1])) % mod;
                ans2 = (ans2 + res[rows + col -1][rows] * ((res[rows + m - col - 1][rows - 1] * ans1) % mod)) % mod;
            }
        }

        swap(n , m);
        for(int rows = 1 ; rows < n ; rows++){
            long ans1 = 0;
            for(int col = m-1 ; col >=0 ; col--){
                ans2 = (ans2 + res[rows + col -1][rows] * ((res[rows + m - col - 1][rows - 1] * ans1) % mod)) % mod;
                ans1 = (ans1 + res[n - rows + m - col -1][n - rows]*(res[n - rows + col - 1][n - rows - 1])) % mod;
            }
        }

        ans2 = (ans2 * 2) % mod;
        System.out.println(ans2);

    }

    public static void swap(int x , int y){
       int temp;
       temp = x;
       x = y;
       y = temp;
    }
}


import java.io.*;
public class Coloring
{
    public static void main(String...args)
    {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = br.readLine();
        String[] st = s.trim().split("\\s+");
        int n = Integer.parseInt(st[0]);
        int m = Integer.parseInt(st[1]);

        if(n < m)
            swap(n , m);

        long mod = 998244353;

        long res[][] = new long[n+m][n];

        for(int i = 0 ; i < n+m ; i++){
            for(int j = 0 ; j < n ; j++){
                if(j == 0)
                    res[i][j] = 1;
                else if(i == 0)
                    res[i][j] = 0;
                else
                    res[i][j] = res[i-1][j-1] + res[i-1][j];
            }
        }

        long ans2 = 0;

        for(int rows = 1 ; rows < n ; rows++){
            long ans1 = 0;
            for(int col = m-1 ; col >=0 ; col--){
                ans1 = (ans1 + res[n - rows + m - col -1][n - rows]*(res[n - rows + col - 1][n - rows - 1])) % mod;
                ans2 = (ans2 + res[rows + col -1][rows] * ((res[rows + m - col - 1][rows - 1] * ans1) % mod)) % mod;
            }
        }

        swap(n , m);
        for(int rows = 1 ; rows < n ; rows++){
            long ans1 = 0;
            for(int col = m-1 ; col >=0 ; col--){
                ans2 = (ans2 + res[rows + col -1][rows] * ((res[rows + m - col - 1][rows - 1] * ans1) % mod)) % mod;
                ans1 = (ans1 + res[n - rows + m - col -1][n - rows]*(res[n - rows + col - 1][n - rows - 1])) % mod;
            }
        }

        ans2 = (ans2 * 2) % mod;
        System.out.println(ans2);

    }

    public void swap(int x , int y){
       int temp;
       temp = x;
       x = y;
       y = temp;
    }
}


import java.io.*;
public class Coloring
{
    public static void main(String...args)throws IOException
    {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = br.readLine();
        String[] st = s.trim().split("\\s+");
        int n = Integer.parseInt(st[0]);
        int m = Integer.parseInt(st[1]);

        if(n < m)
            n= swap(m , m = n);

        long mod = 998244353;

        long res[][] = new long[n+m][n];

        for(int i = 0 ; i < n+m ; i++){
            for(int j = 0 ; j < n ; j++){
                if(j == 0)
                    res[i][j] = 1;
                else if(i == 0)
                    res[i][j] = 0;
                else
                    res[i][j] = (res[i-1][j-1] + res[i-1][j]) % mod;
            }
        }

        long ans2 = 0;

        for(int rows = 1 ; rows < n ; rows++){
            long ans1 = 0;
            for(int col = m-1 ; col >= 1 ; col--){
                ans1 = (ans1 + res[n - rows + m - col -1][n - rows]*(res[n - rows + col - 1][n - rows - 1])) % mod;
                ans2 = (ans2 + res[rows + col -1][rows] * ((res[rows + m - col - 1][rows - 1] * ans1) % mod)) % mod;
            }
        }

        n= swap(m , m = n);
        //System.out.println(n+" "+m);
        for(int rows = 1 ; rows < n ; rows++){
            long ans1 = 0;
            for(int col = m-1 ; col >= 1 ; col--){
                ans2 = (ans2 + res[rows + col -1][rows] * ((res[rows + m - col - 1][rows - 1] * ans1) % mod)) % mod;
                ans1 = (ans1 + res[n - rows + m - col -1][n - rows]*(res[n - rows + col - 1][n - rows - 1])) % mod;
            }
        }

        ans2 = (ans2 * 2) % mod;
        System.out.println(ans2);

    }

    public static int swap(int...args){
        return args[0];
    }
}


//package round712;
import java.io.*;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.InputMismatchException;
import java.util.Queue;

public class E3 {
	InputStream is;
	FastWriter out;
	String INPUT = "";
	final int mod = 998244353;
	long[][] C;

	void solve()
	{
		C = new long[4052 + 1][4052 + 1];
		for (int i = 0; i <= 4052; i++) {
			C[i][0] = 1;
			for (int j = 1; j <= i; j++) {
				C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
				if (C[i][j] >= mod) C[i][j] -= mod;
			}
		}

		int H = ni(), W = ni();

		long T = 0;
		// O(wh^4)
		for(int w = 1;w < W;w++) {
			long R = 0;
			for (int hl = 1; hl <= H; hl++) {
				// 0 <= hr <= H-1-hl
				// d(hl, w-1) * d(hr, w-1)
				// C[dr+w-1][w-1] * d(hl, w-1)
				int hll = H+1 - hl;
				R += d(hll, W-w-1) * C[H-1-hll+W-w][W-w];
				R %= mod;
				T += d(hl, w-1) * C[H-1-hl+w][w] % mod * R;
				T %= mod;
//				for (int hll = 0; hll <= H; hll++) {
//					if (hl + hll > H) {
//						T += d(hl, w-1) * C[H-1-hl+w][w] * d(hll, W-w-1) * C[H-1-hll+W-w][W-w];
//						T %= mod;
//					}
//				}
			}
		}
		T = T * 2;

		long IT = 0;
		// O(hw^4)
		for(int h = 1;h <= H-1;h++){
			// C[h-1+l][l]
			// C[h-1+r][r-1]
			long R = 0;
			for(int r = W-1;r >= 1;r--){
				R += d(H-h-1, r) * C[H-h+W-(r+1)][W-(r+1)];
				R %= mod;
				IT += C[h-1+r][r-1] * d(h-1, W-r) % mod * R;
				IT %= mod;
			}
		}
		IT *= 2;
		out.println((T+IT)%mod);
	}

	long d(int h, int w)
	{
		// begin:h, dec
		return C[h+w][h];
	}

	void run() throws Exception
	{
		is = oj ? System.in : new ByteArrayInputStream(INPUT.getBytes());
		out = new FastWriter(System.out);

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

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

	private byte[] inbuf = new byte[1024];
	public int lenbuf = 0, ptrbuf = 0;

	private int readByte()
	{
		if(lenbuf == -1)throw new InputMismatchException();
		if(ptrbuf >= lenbuf){
			ptrbuf = 0;
			try { lenbuf = is.read(inbuf); } catch (IOException e) { throw new InputMismatchException(); }
			if(lenbuf <= 0)return -1;
		}
		return inbuf[ptrbuf++];
	}

	private boolean isSpaceChar(int c) { return !(c >= 33 && c <= 126); }
	private int skip() { int b; while((b = readByte()) != -1 && isSpaceChar(b)); return b; }

	private double nd() { return Double.parseDouble(ns()); }
	private char nc() { return (char)skip(); }

	private String ns()
	{
		int b = skip();
		StringBuilder sb = new StringBuilder();
		while(!(isSpaceChar(b))){ // when nextLine, (isSpaceChar(b) && b != ' ')
			sb.appendCodePoint(b);
			b = readByte();
		}
		return sb.toString();
	}

	private char[] ns(int n)
	{
		char[] buf = new char[n];
		int b = skip(), p = 0;
		while(p < n && !(isSpaceChar(b))){
			buf[p++] = (char)b;
			b = readByte();
		}
		return n == p ? buf : Arrays.copyOf(buf, p);
	}

	private int[] na(int n)
	{
		int[] a = new int[n];
		for(int i = 0;i < n;i++)a[i] = ni();
		return a;
	}

	private long[] nal(int n)
	{
		long[] a = new long[n];
		for(int i = 0;i < n;i++)a[i] = nl();
		return a;
	}

	private char[][] nm(int n, int m) {
		char[][] map = new char[n][];
		for(int i = 0;i < n;i++)map[i] = ns(m);
		return map;
	}

	private int[][] nmi(int n, int m) {
		int[][] map = new int[n][];
		for(int i = 0;i < n;i++)map[i] = na(m);
		return map;
	}

	private int ni() { return (int)nl(); }

	private long nl()
	{
		long num = 0;
		int b;
		boolean minus = false;
		while((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
		if(b == '-'){
			minus = true;
			b = readByte();
		}

		while(true){
			if(b >= '0' && b <= '9'){
				num = num * 10 + (b - '0');
			}else{
				return minus ? -num : num;
			}
			b = readByte();
		}
	}

	public static class FastWriter
	{
		private static final int BUF_SIZE = 1<<13;
		private final byte[] buf = new byte[BUF_SIZE];
		private final OutputStream out;
		private int ptr = 0;

		private FastWriter(){out = null;}

		public FastWriter(OutputStream os)
		{
			this.out = os;
		}

		public FastWriter(String path)
		{
			try {
				this.out = new FileOutputStream(path);
			} catch (FileNotFoundException e) {
				throw new RuntimeException("FastWriter");
			}
		}

		public FastWriter write(byte b)
		{
			buf[ptr++] = b;
			if(ptr == BUF_SIZE)innerflush();
			return this;
		}

		public FastWriter write(char c)
		{
			return write((byte)c);
		}

		public FastWriter write(char[] s)
		{
			for(char c : s){
				buf[ptr++] = (byte)c;
				if(ptr == BUF_SIZE)innerflush();
			}
			return this;
		}

		public FastWriter write(String s)
		{
			s.chars().forEach(c -> {
				buf[ptr++] = (byte)c;
				if(ptr == BUF_SIZE)innerflush();
			});
			return this;
		}

		private static int countDigits(int l) {
			if (l >= 1000000000) return 10;
			if (l >= 100000000) return 9;
			if (l >= 10000000) return 8;
			if (l >= 1000000) return 7;
			if (l >= 100000) return 6;
			if (l >= 10000) return 5;
			if (l >= 1000) return 4;
			if (l >= 100) return 3;
			if (l >= 10) return 2;
			return 1;
		}

		public FastWriter write(int x)
		{
			if(x == Integer.MIN_VALUE){
				return write((long)x);
			}
			if(ptr + 12 >= BUF_SIZE)innerflush();
			if(x < 0){
				write((byte)'-');
				x = -x;
			}
			int d = countDigits(x);
			for(int i = ptr + d - 1;i >= ptr;i--){
				buf[i] = (byte)('0'+x%10);
				x /= 10;
			}
			ptr += d;
			return this;
		}

		private static int countDigits(long l) {
			if (l >= 1000000000000000000L) return 19;
			if (l >= 100000000000000000L) return 18;
			if (l >= 10000000000000000L) return 17;
			if (l >= 1000000000000000L) return 16;
			if (l >= 100000000000000L) return 15;
			if (l >= 10000000000000L) return 14;
			if (l >= 1000000000000L) return 13;
			if (l >= 100000000000L) return 12;
			if (l >= 10000000000L) return 11;
			if (l >= 1000000000L) return 10;
			if (l >= 100000000L) return 9;
			if (l >= 10000000L) return 8;
			if (l >= 1000000L) return 7;
			if (l >= 100000L) return 6;
			if (l >= 10000L) return 5;
			if (l >= 1000L) return 4;
			if (l >= 100L) return 3;
			if (l >= 10L) return 2;
			return 1;
		}

		public FastWriter write(long x)
		{
			if(x == Long.MIN_VALUE){
				return write("" + x);
			}
			if(ptr + 21 >= BUF_SIZE)innerflush();
			if(x < 0){
				write((byte)'-');
				x = -x;
			}
			int d = countDigits(x);
			for(int i = ptr + d - 1;i >= ptr;i--){
				buf[i] = (byte)('0'+x%10);
				x /= 10;
			}
			ptr += d;
			return this;
		}

		public FastWriter write(double x, int precision)
		{
			if(x < 0){
				write('-');
				x = -x;
			}
			x += Math.pow(10, -precision)/2;
			//		if(x < 0){ x = 0; }
			write((long)x).write(".");
			x -= (long)x;
			for(int i = 0;i < precision;i++){
				x *= 10;
				write((char)('0'+(int)x));
				x -= (int)x;
			}
			return this;
		}

		public FastWriter writeln(char c){
			return write(c).writeln();
		}

		public FastWriter writeln(int x){
			return write(x).writeln();
		}

		public FastWriter writeln(long x){
			return write(x).writeln();
		}

		public FastWriter writeln(double x, int precision){
			return write(x, precision).writeln();
		}

		public FastWriter write(int... xs)
		{
			boolean first = true;
			for(int x : xs) {
				if (!first) write(' ');
				first = false;
				write(x);
			}
			return this;
		}

		public FastWriter write(long... xs)
		{
			boolean first = true;
			for(long x : xs) {
				if (!first) write(' ');
				first = false;
				write(x);
			}
			return this;
		}

		public FastWriter writeln()
		{
			return write((byte)'\n');
		}

		public FastWriter writeln(int... xs)
		{
			return write(xs).writeln();
		}

		public FastWriter writeln(long... xs)
		{
			return write(xs).writeln();
		}

		public FastWriter writeln(char[] line)
		{
			return write(line).writeln();
		}

		public FastWriter writeln(char[]... map)
		{
			for(char[] line : map)write(line).writeln();
			return this;
		}

		public FastWriter writeln(String s)
		{
			return write(s).writeln();
		}

		private void innerflush()
		{
			try {
				out.write(buf, 0, ptr);
				ptr = 0;
			} catch (IOException e) {
				throw new RuntimeException("innerflush");
			}
		}

		public void flush()
		{
			innerflush();
			try {
				out.flush();
			} catch (IOException e) {
				throw new RuntimeException("flush");
			}
		}

		public FastWriter print(byte b) { return write(b); }
		public FastWriter print(char c) { return write(c); }
		public FastWriter print(char[] s) { return write(s); }
		public FastWriter print(String s) { return write(s); }
		public FastWriter print(int x) { return write(x); }
		public FastWriter print(long x) { return write(x); }
		public FastWriter print(double x, int precision) { return write(x, precision); }
		public FastWriter println(char c){ return writeln(c); }
		public FastWriter println(int x){ return writeln(x); }
		public FastWriter println(long x){ return writeln(x); }
		public FastWriter println(double x, int precision){ return writeln(x, precision); }
		public FastWriter print(int... xs) { return write(xs); }
		public FastWriter print(long... xs) { return write(xs); }
		public FastWriter println(int... xs) { return writeln(xs); }
		public FastWriter println(long... xs) { return writeln(xs); }
		public FastWriter println(char[] line) { return writeln(line); }
		public FastWriter println(char[]... map) { return writeln(map); }
		public FastWriter println(String s) { return writeln(s); }
		public FastWriter println() { return writeln(); }
	}

	public void trnz(int... o)
	{
		for(int i = 0;i < o.length;i++)if(o[i] != 0)System.out.print(i+":"+o[i]+" ");
		System.out.println();
	}

	// print ids which are 1
	public void trt(long... o)
	{
		Queue<Integer> stands = new ArrayDeque<>();
		for(int i = 0;i < o.length;i++){
			for(long x = o[i];x != 0;x &= x-1)stands.add(i<<6|Long.numberOfTrailingZeros(x));
		}
		System.out.println(stands);
	}

	public void tf(boolean... r)
	{
		for(boolean x : r)System.out.print(x?'#':'.');
		System.out.println();
	}

	public void tf(boolean[]... b)
	{
		for(boolean[] r : b) {
			for(boolean x : r)System.out.print(x?'#':'.');
			System.out.println();
		}
		System.out.println();
	}

	public void tf(long[]... b)
	{
		if(INPUT.length() != 0) {
			for (long[] r : b) {
				for (long x : r) {
					for (int i = 0; i < 64; i++) {
						System.out.print(x << ~i < 0 ? '#' : '.');
					}
				}
				System.out.println();
			}
			System.out.println();
		}
	}

	public void tf(long... b)
	{
		if(INPUT.length() != 0) {
			for (long x : b) {
				for (int i = 0; i < 64; i++) {
					System.out.print(x << ~i < 0 ? '#' : '.');
				}
			}
			System.out.println();
		}
	}

	private boolean oj = System.getProperty("ONLINE_JUDGE") != null;
	private void tr(Object... o) { if(!oj)System.out.println(Arrays.deepToString(o)); }
}


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.io.BufferedWriter;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.util.InputMismatchException;
import java.io.IOException;
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;
        InputReader in = new InputReader(inputStream);
        OutputWriter out = new OutputWriter(outputStream);
        TaskC solver = new TaskC();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskC {
        int n;
        char[] colorCard;
        int[] costRedToken;
        int[] costBlueToken;
        final int inf = (int) 1e9;

        public void solve(int testNumber, InputReader in, OutputWriter out) {
            n = in.readInt();
            colorCard = new char[n];
            costRedToken = new int[n];
            costBlueToken = new int[n];

            int overRed = 0;
            int overBlue = 0;

            for (int i = 0; i < n; i++) {
                colorCard[i] = in.readCharacter();

                costRedToken[i] = in.readInt();
                costBlueToken[i] = in.readInt();

                overRed += Math.max(costRedToken[i] - n, 0);
                overBlue += Math.max(costBlueToken[i] - n, 0);

                costRedToken[i] = Math.min(n, costRedToken[i]);
                costBlueToken[i] = Math.min(n, costBlueToken[i]);
            }

            int over = Math.max(overRed, overBlue);

            int[][][] dp = new int[2][n * n + 1][1 << n];
            ArrayUtils.fill(dp, inf);

            if (overRed > overBlue) {
                dp[0][Math.min(overRed, n * n)][0] = 0;
            } else {
                dp[1][Math.min(overBlue, n * n)][0] = 0;
            }

            for (int msk = 0; msk < (1 << n); msk++) {
                for (int typeRemToken = 0; typeRemToken < 2; typeRemToken++) {
                    for (int countRemTokens = 0; countRemTokens <= n * n; countRemTokens++) {

                        if (dp[typeRemToken][countRemTokens][msk] == inf) {
                            continue;
                        }

                        int countRedCard = 0;
                        int countBlueCard = 0;
                        for (int card = 0; card < n; card++) {
                            if (((msk >> card) & 1) == 1) {
                                if (colorCard[card] == 'R') {
                                    ++countRedCard;
                                } else if (colorCard[card] == 'B') {
                                    ++countBlueCard;
                                } else {
                                    throw new RuntimeException();
                                }
                            }
                        }

                        for (int card = 0; card < n; card++) {
                            if (((msk >> card) & 1) == 1) {
                                continue;
                            }

                            int countHaveRedToken = typeRemToken == 0 ? countRemTokens : 0;
                            int countHaveBlueToken = typeRemToken == 1 ? countRemTokens : 0;

                            int costRed = Math.max(costRedToken[card] - countRedCard, 0);
                            int costBlue = Math.max(costBlueToken[card] - countBlueCard, 0);

                            int countBuyToken = Math.max(Math.max(costRed - countHaveRedToken, 0), Math.max(costBlue -
                                    countHaveBlueToken, 0));

                            int remRedToken = countBuyToken + countHaveRedToken - costRed;
                            int remBlueToken = countBuyToken + countHaveBlueToken - costBlue;

                            int toMsk = msk | (1 << card);
                            int toTypeRemToken = remRedToken == 0 ? 1 : 0;
                            int toCountRemTokens = Math.max(remRedToken, remBlueToken);

                            dp[toTypeRemToken][toCountRemTokens][toMsk] = Math.min
                                    (dp[toTypeRemToken][toCountRemTokens][toMsk],
                                            dp[typeRemToken][countRemTokens][msk] + countBuyToken);

                        }
                    }
                }
            }

            int curAns = inf;
            for (int typeRemToken = 0; typeRemToken < 2; typeRemToken++) {
                for (int countRemTokens = 0; countRemTokens <= n * n; countRemTokens++) {
                    curAns = Math.min(curAns, dp[typeRemToken][countRemTokens][(1 << n) - 1]);
                }
            }
            out.printLine(n + over + curAns);
//        out.printLine(dp[0][1][0b1]);
//        out.printLine(dp[0][1][0b11]);
//        out.printLine(dp[0][1][0b111]);
        }

    }

    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 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 char readCharacter() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            return (char) c;
        }

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

        }

    }

    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 printLine(int i) {
            writer.println(i);
        }

    }

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

        public static void fill(int[][][] array, int value) {
            for (int[][] subArray : array) {
                fill(subArray, value);
            }
        }

    }
}