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

public class PE{
	final static long MOD=1000000007L;
	static long v[], k, w;
	static Scanner sc=new Scanner(System.in);
	static {
		v=new long[1000001];
		v[0] = 0; v[1] = 1;
		//for(int i=2; i <= 1000000; i++) v[i]=modPow(i,MOD-2);
		for(int i=2; i <= 1000000; i++) v[i]=v[(int)MOD%i]*(MOD-MOD/i)%MOD;
	}
	static long modPow(long a, long x) // a^x
	{
		long ret = 1L;
		for(; x>0; x>>=1, a=a*a%MOD) if(1==(x&1))ret=ret*a%MOD;
		return ret;
	}
	static long f(long x)
	{
		long ret=0L, s=modPow(k,x);
		for(int i = 0; i < k; i++)
		{
			ret=(ret+s)%MOD;
			s=s*v[(int)k+(int)(x>i+2?0L:x-i-2)]%MOD*(k-i-1)%MOD*(k-i-1)%MOD;
		}
		return ret;
	}
	public static void main(String args[]) throws Exception
	{
//		int ncases=sc.nextInt();
//		while(ncases-->0)
		{
			k=sc.nextLong();
			w=sc.nextLong();
			System.out.println((f(w+1)-f(w)+MOD+modPow(k,w))%MOD);
		}
	}
}


/*
I am dead inside
Do you like NCT, sKz, BTS?
5 4 3 2 1 Moonwalk
Imma knock it down like domino
Is this what you want? Is this what you want?
Let's ttalkbocky about that
 */
import static java.lang.Math.*;
import java.util.*;
import java.io.*;
import java.math.*;

public class lolE
{
    static String target = "theseus";
    static String reverse = "sueseth";
    //static int[] dx = {-1, 1, 0, 0, -1, -1, 1, 1};
    //static int[] dy = {0, 0, -1, 1, -1, 1, -1, 1};
    //static int[] dx = {0, 1, 1};
    //static int[] dy = {1, 0, 1};
    public static void main(String omkar[]) throws Exception
    {
        BufferedReader infile = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(infile.readLine());
        int N = Integer.parseInt(st.nextToken());
        char[][] grid = new char[N][N];
        for(int r=0; r < N; r++)
            grid[r] = infile.readLine().trim().toCharArray();
        String res = "NO";
        for(int r=0; r < N; r++)
            for(int c=0; c < N-6; c++)
            {
                String temp = "";
                for(int i=c; i < c+7; i++)
                    temp += grid[r][i];
                if(temp.equals(target))
                    res = "YES";
            }
        for(int c=0; c < N; c++)
            for(int r=0; r < N-6; r++)
            {
                String temp = "";
                for(int i=r; i < r+7; i++)
                    temp += grid[i][c];
                if(temp.equals(target))
                    res = "YES";
            }
        //diagonals
        for(int r=0; r < N; r++)
            for(int c=0; c < N; c++)
            {
                String temp = "";
                int lol = 0;
                while(temp.length() < 7)
                {
                    if(max(r, c)+lol >= N)
                        break;
                    temp += grid[r+lol][c+lol];
                    lol++;
                }
                if(temp.equals(target))
                    res = "YES";
            }
        System.out.println(res);
    }
}


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

public class E {
	FastScanner in;
	PrintWriter out;

	final int mod = (int) 1e9 + 7;

	void solve() {
		int n = in.nextInt();
		int[] a = new int[n - 1];
		for (int i = 0; i < n - 1; i++) {
			a[i] = in.nextInt();
		}
		int[] b = new int[n];
		for (int i = 0; i < n; i++) {
			b[i] = in.nextInt();
		}
		BigInteger need = new BigInteger(in.next());
		int[] needA = new int[n];
		for (int i = 0; i + 1 < n; i++) {
			if (a[i] != 1) {
				needA[i] = need.mod(BigInteger.valueOf(a[i])).intValue();
				need = need.divide(BigInteger.valueOf(a[i]));
			}
		}
		if (need.compareTo(BigInteger.valueOf((int) 1e9)) > 0) {
			out.println(0);
			return;
		}
		needA[n - 1] = need.intValue();
		int[] cnt = new int[] { 1 };
		int[] sum = new int[] { 1 };
		for (int i = 0; i < n;) {
			int j = i;
			int sumBI = 1 + b[i];
			while (j != n - 1 && a[j] == 1) {
				j++;
				sumBI += b[j];
			}
			int div = i == 0 ? 1 : a[i - 1];
			sumBI += (1 + sum.length / div);
			int[] nextSum = new int[sumBI];
			int[] nextCnt = new int[sumBI];
			int beforeNeed = i == 0 ? 0 : needA[i - 1];
			int it = 0;
			for (int k = beforeNeed; k < cnt.length; k += div) {
				nextCnt[it++] = cnt[k];
			}
			updateSum(nextCnt, nextSum);
			for (int k = i; k <= j; k++) {
				int how = b[k];
				for (int l = 0; l < nextCnt.length; l++) {
					nextCnt[l] = nextSum[l];
					if (l - how - 1 >= 0) {
						nextCnt[l] -= nextSum[l - how - 1];
						if (nextCnt[l] < 0) {
							nextCnt[l] += mod;
						}
					}
				}
				updateSum(nextCnt, nextSum);
			}
			i = j + 1;
			sum = nextSum;
			cnt = nextCnt;
		}
		if (needA[n - 1] >= cnt.length) {
			out.println(0);
		} else {
			out.println(cnt[needA[n - 1]]);
		}
	}

	void updateSum(int[] val, int[] sum) {
		sum[0] = val[0];
		for (int i = 1; i < sum.length; i++) {
			sum[i] = sum[i - 1] + val[i];
			if (sum[i] >= mod) {
				sum[i] -= mod;
			}
		}
	}

	void run() {
		try {
			in = new FastScanner(new File("object.in"));
			out = new PrintWriter(new File("object.out"));

			solve();

			out.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	void runIO() {

		in = new FastScanner(System.in);
		out = new PrintWriter(System.out);

		solve();

		out.close();
	}

	class FastScanner {
		BufferedReader br;
		StringTokenizer st;

		public FastScanner(File f) {
			try {
				br = new BufferedReader(new FileReader(f));
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}

		public FastScanner(InputStream f) {
			br = new BufferedReader(new InputStreamReader(f));
		}

		String next() {
			while (st == null || !st.hasMoreTokens()) {
				String s = null;
				try {
					s = br.readLine();
				} catch (IOException e) {
					e.printStackTrace();
				}
				if (s == null)
					return null;
				st = new StringTokenizer(s);
			}
			return st.nextToken();
		}

		boolean hasMoreTokens() {
			while (st == null || !st.hasMoreTokens()) {
				String s = null;
				try {
					s = br.readLine();
				} catch (IOException e) {
					e.printStackTrace();
				}
				if (s == null)
					return false;
				st = new StringTokenizer(s);
			}
			return true;
		}

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

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

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

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


import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Locale;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
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);
		PrintWriter out = new PrintWriter(outputStream);
		TaskE solver = new TaskE();
		solver.solve(1, in, out);
		out.close();
	}
}

class TaskE {
    static class Query {
        int kind;
        Vertex v;
        Vertex u;
        int t;
    }

    static class Vertex {
        long s;
        long subtreeSize;
        boolean hasInterestingInSubtree = false;
        long subtreeAnswer;
        long childSquareSum;
        Vertex parent;
        Vertex firstChild;
        Vertex nextSibling;
        Vertex interestingParent;
        Vertex firstInterestingChild;
        Vertex nextInterestingSibling;
        long multiplierForIntermediates;
        long constantForIntermediates;
        long intermediatesSize;
        long boringChildrenSize;
        long boringChildrenSquareSum;
        long boringChildrenAnswer;
        boolean interesting;

        void propagateInteresting() {
            Vertex v = firstChild;
            int numIntChild = 0;
            while (v != null) {
                v.propagateInteresting();
                if (v.hasInterestingInSubtree) ++numIntChild;
                v = v.nextSibling;
            }
            if (numIntChild >= 2) interesting = true;
            if (interesting || numIntChild >= 1) hasInterestingInSubtree = true;
        }

        void init() {
            subtreeSize = 1;
            subtreeAnswer = 0;
            childSquareSum = 0;
            boringChildrenSize = 0;
            boringChildrenAnswer = 0;
            boringChildrenSquareSum = 0;
            Vertex v = firstChild;
            while (v != null) {
                v.init();
                subtreeSize += v.subtreeSize;
                subtreeAnswer += v.subtreeAnswer;
                childSquareSum += v.subtreeSize * (long) v.subtreeSize;
                if (interesting && !v.hasInterestingInSubtree) {
                    boringChildrenSize += v.subtreeSize;
                    boringChildrenAnswer += v.subtreeAnswer;
                    boringChildrenSquareSum += v.subtreeSize * (long) v.subtreeSize;
                }
                v = v.nextSibling;
            }
            subtreeAnswer += (subtreeSize * (long) subtreeSize - childSquareSum) * s;
        }

        public void interestingInit() {
            subtreeSize = 1 + boringChildrenSize;
            subtreeAnswer = boringChildrenAnswer;
            childSquareSum = boringChildrenSquareSum;
            Vertex v = firstInterestingChild;
            while (v != null) {
                v.interestingInit();
                long childSize = v.subtreeSize + v.intermediatesSize;
                subtreeSize += childSize;
                subtreeAnswer += v.subtreeAnswer;
                subtreeAnswer += v.multiplierForIntermediates * v.subtreeSize + v.constantForIntermediates;
                childSquareSum += childSize * (long) childSize;
                v = v.nextInterestingSibling;
            }
            subtreeAnswer += (subtreeSize * (long) subtreeSize - childSquareSum) * s;
        }

        public void initIntermediates() {
            Vertex tmp = this;
            intermediatesSize = 0;
            multiplierForIntermediates = 0;
            constantForIntermediates = 0;
            while (!tmp.parent.interesting) {
                long curExtra = tmp.parent.subtreeSize - tmp.subtreeSize;
                intermediatesSize += curExtra;
                multiplierForIntermediates += 2 * curExtra * (long) tmp.parent.s;
                constantForIntermediates += tmp.parent.subtreeAnswer - tmp.subtreeAnswer - 2 * subtreeSize * (long) (tmp.parent.subtreeSize - tmp.subtreeSize) * tmp.parent.s;
                tmp = tmp.parent;
            }
            interestingParent = tmp.parent;
        }
    }

    public void solve(int testNumber, InputReader in, PrintWriter out) {
        /*char[] buf = new char[100000];
        int size;
        try {
            size = in.reader.read(buf);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        if (size == buf.length) throw new RuntimeException();
        String input = new String(buf, 0, size);
        StringWriter sw1 = new StringWriter();
        PrintWriter pw1 = new PrintWriter(sw1);
        doit(new InputReader(new StringInputStream(input)), pw1, 1);
        pw1.close();
        StringWriter sw2 = new StringWriter();
        PrintWriter pw2 = new PrintWriter(sw2);
        doit(new InputReader(new StringInputStream(input)), pw2, 500);
        pw2.close();
        String s1 = sw1.toString();
        String s2 = sw2.toString();
        if (!s1.equals(s2)) throw new RuntimeException();
        out.print(s1);*/
        doit(in, out, 500);
    }

    private void doit(InputReader in, PrintWriter out, int BUBEN) {
        Locale.setDefault(Locale.US);
        int n = in.nextInt();
        Vertex[] vs = new Vertex[n];
        for (int i = 0; i < n; ++i) vs[i] = new Vertex();
        for (int i = 1; i < n; ++i) {
            vs[i].parent = vs[in.nextInt() - 1];
        }
        for (int i = 0; i < n; ++i) {
            vs[i].s = in.nextInt();
        }
        Vertex root = vs[0];
        int numQueries = in.nextInt();
        Query[] queries = new Query[numQueries];
        for (int i = 0; i < numQueries; ++i) {
            Query query = new Query();
            String ss = in.next();
            if (ss.equals("P")) query.kind = 1; else if (ss.equals("V")) query.kind = 2; else throw new RuntimeException();
            if (query.kind == 1) {
                query.v = vs[in.nextInt() - 1];
                query.u = vs[in.nextInt() - 1];
            } else {
                query.v = vs[in.nextInt() - 1];
                query.t = in.nextInt();
            }
            queries[i] = query;
        }
        double mult = 1.0 / n / n;
        Vertex[] inters = new Vertex[n];
        for (int blockFirst = 0; blockFirst < numQueries; blockFirst += BUBEN) {
            int blockLast = Math.min(numQueries, blockFirst + BUBEN);
            for (Vertex vv : vs) {
                vv.interesting = false;
                vv.hasInterestingInSubtree = false;
            }
            root.interesting = true;
            for (int qid = blockFirst; qid < blockLast; ++qid) {
                Query query = queries[qid];
                if (query.kind == 1) {
                    query.v.interesting = true;
                    if (query.v.parent != null) query.v.parent.interesting = true;
                    query.u.interesting = true;
                    if (query.u.parent != null) query.u.parent.interesting = true;
                } else {
                    query.v.interesting = true;
                }
            }
            for (Vertex vv : vs) {
                vv.firstChild = null;
                vv.nextSibling = null;
            }
            for (Vertex vv : vs) {
                if (vv.parent != null) {
                    vv.nextSibling = vv.parent.firstChild;
                    vv.parent.firstChild = vv;
                }
            }
            root.propagateInteresting();
            int ninters = 0;
            for (Vertex vv : vs) if (vv.interesting) {
                inters[ninters++] = vv;
            }
            root.init();
            for (int i = 0; i < ninters; ++i) {
                Vertex vv = inters[i];
                if (vv.parent != null) vv.initIntermediates();
            }
            if (blockFirst == 0) {
                out.println(String.format("%.12f", root.subtreeAnswer * mult));
            }
            for (int qid = blockFirst; qid < blockLast; ++qid) {
                Query query = queries[qid];
                if (query.kind == 1) {
                    Vertex tmp = query.u;
                    while (tmp != null && tmp != query.v) tmp = tmp.interestingParent;
                    if (tmp == null) {
                        query.v.parent = query.u;
                        query.v.interestingParent = query.u;
                    } else {
                        query.u.parent = query.v;
                        query.u.interestingParent = query.v;
                    }
                } else {
                    query.v.s = query.t;
                }
                for (int i = 0; i < ninters; ++i) {
                    Vertex vv = inters[i];
                    vv.firstInterestingChild = null;
                    vv.nextInterestingSibling = null;
                }
                for (int i = 0; i < ninters; ++i) {
                    Vertex vv = inters[i];
                    if (vv.interestingParent != null) {
                        vv.nextInterestingSibling = vv.interestingParent.firstInterestingChild;
                        vv.interestingParent.firstInterestingChild = vv;
                    }
                }
                root.interestingInit();
                out.println(String.format("%.12f", root.subtreeAnswer * mult));
            }
        }
    }
}

class InputReader {
    public BufferedReader reader;
    public StringTokenizer tokenizer;

    public InputReader(InputStream stream) {
        reader = new BufferedReader(new InputStreamReader(stream), 32768);
        tokenizer = null;
    }

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

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

}


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

// you can compare with output.txt and expected out
public class RoundEdu132F {
	MyPrintWriter out;
	MyScanner in;

//	final static long FIXED_RANDOM;
//	static {
//		FIXED_RANDOM = System.currentTimeMillis();
//	}
	final static String IMPOSSIBLE = "IMPOSSIBLE";
	final static String POSSIBLE = "POSSIBLE";
	final static String YES = "YES";
	final static String NO = "NO";

    private void initIO(boolean isFileIO) {
        if (System.getProperty("ONLINE_JUDGE") == null && isFileIO) {
        	try{
	            in = new MyScanner(new FileInputStream("input.txt"));
	            out = new MyPrintWriter(new FileOutputStream("output.txt"));
        	}
        	catch(FileNotFoundException e){
        		e.printStackTrace();
        	}
        }
        else{
    		in = new MyScanner(System.in);
    	    out = new MyPrintWriter(new BufferedOutputStream(System.out));
        }
    }

	public static void main(String[] args){
//		Scanner in = new Scanner(new BufferedReader(new InputStreamReader(System.in)));

		RoundEdu132F sol = new RoundEdu132F();
		sol.run();
    }

	private void run() {
		boolean isDebug = false;
		boolean isFileIO = true;
		boolean hasMultipleTests = false;

		initIO(isFileIO);

		int t = hasMultipleTests? in.nextInt() : 1;

	    for (int i = 1; i <= t; ++i) {
	    	if(isDebug){
		    	out.printf("Test %d\n", i);
	    	}

	    	getInput();

	    	ans = solve();

	    	printOutput();
	    }

	    in.close();
	    out.close();
	}

	int n, k, f;
	void getInput() {
		n = in.nextInt();
		k = in.nextInt();
		f = in.nextInt();
	}

	void printOutput() {
		out.printlnAns(ans);
	}

	static final long MOD = 998244353;
	int ans;
	int solve(){
		long ans = 0;

		// for each binary string s,
		// consider binary strings t of length n, where s is a prefix of t
		// we can have <= c_s among t

		// it's a full binary tree
		// where each vertex contains # of records in its subtree
		// and all records are at depth n

		// from each leaf, go up again, update each vertex to min(val, val(child1) + val(child2))
		// then the val(root) must be exactly f
		// where root is null string

		// the problem in this counting is we would have to use inclusion-exclusion due to duplicate cases
		// so we choose vertices representing disjoint subtrees
		// whose union can represent the whole tree
		// and whose sum is exactly f

		// for vertices under the subtrees, we can freely choose val
		// so that sum of siblings in each subtree >= val of root of subtree
		// for vertices above the subtrees, we can freely choose val between sum and k

		// so another approach:
		// for each vertex v of depth h
		// find # of ways to make sum exactly x
		// case 1: val(v) = x
		// add up ways(l, y)*ways(r, z) where y+z >= x
		// case 2: val(v) >= x+1
		// add up ways(l, y)*ways(r, z) where y+z = x

		// 2 1 1
		// case 1: c_0 = 1, c_1 = 0
		// 00 01 10 11
		// 1  0  0/1 0/1
		// 0  1  0/1 0/1
		// 1  1  0/1 0/1
		// -> 12

		// case 2: c_0 = 0, c_1 = 1
		// -> 12

		// case 3: c_0 = 1, c_1 = 1
		// 1 0 0 0
		// 0 1 0 0
		// 1 1 0 0
		// 0 0 1 0
		// 0 0 0 1
		// 0 0 1 1
		// -> 6

		// subtree_0 = 1 -> 3
		// c_0 = 1 -> 1 0, 0 1, 1 1

		// subtree_0 = 0 -> 5
		// c_0 = 0 -> 0/1, 0/1 -> 4
		// c_0 = 1 -> 0, 0 -> 1
		// 3*5 + 3*5 = 30

		// subtree_l = 0 -> 1
		// subtree_l = 1 -> 1

		// long[] ways = new long[f+2];
		int len = 1;
		while (len < f+2 + f+2)
	        len <<= 1;
		long[] ways = new long[len];
		long[] suffix = new long[f+2];

		for(int i=0; i<=k && i<=f; i++)
			ways[i] = 1;
		ways[f+1] = Math.max(0, k-f);

		for(int i=0; i<n; i++) {
			// long[] mult = multiplyPolynomial(ways, ways);
			if(i < n-1) {
				square(ways);
				{
					long val = 0;
					for(int j=ways.length-1; j>=f+1; j--)
						val += ways[j];
					val %= MOD;
					suffix[f+1] = val;
				}
				for(int j=f; j>=0; j--){
					long val = suffix[j+1] + ways[j];
					val = val>=MOD? val-MOD: val;
					suffix[j] = val;
				}

				for(int j=0; j <= k && j<=f; j++) {
					long val = suffix[j]; // c_curr = j
					val += ways[j]*(k-j); // j+1 ~ k
					val %= MOD;
					ways[j] = val;
				}
				{
					long val = suffix[f+1]*Math.max(0, k-f);
					val %= MOD;
					ways[f+1] = val;
				}
				Arrays.fill(ways, f+2, ways.length, 0);
			}
			else {
				for(int j=0; j<=f; j++)
					ans += ways[j]*ways[f-j] % MOD;
				ans %= MOD;
			}
		}

		return (int)ans;
	}

	static long pow(int a, int k, long p) {
		long m = k;
		long ans = 1;

		// curr = k^(2^i)
		long curr = a;

		// k^(2x+1) = (k^x)^2 * k
		while(m > 0) {
			if( (m&1) == 1 ) {
				ans *= curr;
				ans %= p;
			}
			m >>= 1;
			curr *= curr;
			curr %= p;
		}
		return ans;
	}

	// computes a^(p-2)
	static long inverse(int a, long p) {
		return pow(a, (int)(p-2), p);
	}

	// static final long MOD = 998_244_353; // = 1 + 7*17*2^23
	// primitive root is 3, i.e., the order of 3 is MOD-1
	// we have to find the 1<<23-th root
	static final int ROOT = (int) pow(3, 7*17, MOD);
	static final int ROOT_INV = (int) pow(ROOT, (int)MOD - 2, MOD);
	static final int ORDER = 1 << 23;

	static void swap(long[] a, int i, int j) {
		long temp = a[i];
		a[i] = a[j];
		a[j] = temp;
	}
	static void fft(long[] a, boolean isInvertedFFT) {
	    int n = a.length;

	    for (int i = 1, j = 0; i < n; i++) {
	        int bit = n >> 1;
	        for (; (j & bit) > 0; bit >>= 1)
	            j ^= bit;
	        j ^= bit;

	        if (i < j) {
	        	swap(a, i, j);
	        }
	    }

	    for (int len = 2; len <= n; len <<= 1) {
	        long wlen = isInvertedFFT ? ROOT_INV : ROOT;
	        for (int i = len; i < ORDER; i <<= 1)
	            wlen = wlen * wlen % MOD;

	        for (int i = 0; i < n; i += len) {
	        	long w = 1;
	            for (int j = 0; j < len / 2; j++) {
	                long u = a[i+j];
	                long v = a[i+j+len/2] * w % MOD;
	                a[i+j] =  u + v < MOD ? u + v : u + v - MOD;
	                a[i+j+len/2] = u - v >= 0 ? u - v : u - v + MOD;
	                w = w * wlen % MOD;
	            }
	        }
	    }

	    if (isInvertedFFT) {
	        long inv = inverse(n, MOD);
	        for(int i=0; i<a.length; i++)
	        	a[i] = a[i]*inv%MOD;
	    }
	}

	static void square(long[] a) {
	    fft(a, false);
	    for (int i = 0; i < a.length; i++)
	        a[i] = a[i]*a[i] % MOD;
	    fft(a, true);
	}

	static long[] multiplyPolynomial(long[] a, long[] b) {
	    int n = 1;
	    while (n < a.length + b.length)
	        n <<= 1;
		long[] fa = Arrays.copyOf(a, n);
		long[] fb = Arrays.copyOf(b, n);

	    fft(fa, false);
	    fft(fb, false);
	    for (int i = 0; i < n; i++)
	        fa[i] = fa[i]*fb[i] % MOD;
	    fft(fa, true);

	    return fa;
	}

	public static class MyScanner {
		BufferedReader br;
		StringTokenizer st;

		// 32768?
		public MyScanner(InputStream is, int bufferSize) {
			br = new BufferedReader(new InputStreamReader(is), bufferSize);
		}

		public MyScanner(InputStream is) {
			br = new BufferedReader(new InputStreamReader(is));
			// br = new BufferedReader(new InputStreamReader(System.in));
			// br = new BufferedReader(new InputStreamReader(new FileInputStream("input.txt")));
		}

		public void close() {
			try {
				br.close();
			} catch (IOException e) {
				e.printStackTrace();
			}

		}

		String next() {
			while (st == null || !st.hasMoreElements()) {
				try {
					st = new StringTokenizer(br.readLine());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			return st.nextToken();
		}

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

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

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

		String nextLine(){
			String str = "";
			try {
				str = br.readLine();
			} catch (IOException e) {
				e.printStackTrace();
			}
			return str;
		}

		int[][] nextTreeEdges(int n, int offset){
			int[][] e = new int[n-1][2];
			for(int i=0; i<n-1; i++){
				e[i][0] = nextInt()+offset;
				e[i][1] = nextInt()+offset;
			}
			return e;
		}

		int[][] nextMatrix(int n, int m) {
			return nextMatrix(n, m, 0);
		}

		int[][] nextMatrix(int n, int m, int offset) {
			int[][] mat = new int[n][m];
			for(int i=0; i<n; i++) {
				for(int j=0; j<m; j++) {
					mat[i][j] = nextInt()+offset;
				}
			}
			return mat;
		}

		int[][] nextPairs(int n){
			return nextPairs(n, 0);
		}

		int[][] nextPairs(int n, int offset) {
			int[][] xy = new int[2][n];
			for(int i=0; i<n; i++) {
				xy[0][i] = nextInt() + offset;
				xy[1][i] = nextInt() + offset;
			}
			return xy;
		}

		int[][] nextGraphEdges(){
			return nextGraphEdges(0);
		}

		int[][] nextGraphEdges(int offset) {
			int m = nextInt();
			int[][] e = new int[m][2];
			for(int i=0; i<m; i++){
				e[i][0] = nextInt()+offset;
				e[i][1] = nextInt()+offset;
			}
			return e;
		}

		int[] nextIntArray(int len) {
			return nextIntArray(len, 0);
		}
		int[] nextIntArray(int len, int offset){
	    	int[] a = new int[len];
			for(int j=0; j<len; j++)
	    		a[j] = nextInt()+offset;
	    	return a;
		}

		long[] nextLongArray(int len) {
			return nextLongArray(len, 0);
		}
		long[] nextLongArray(int len, int offset){
	    	long[] a = new long[len];
			for(int j=0; j<len; j++)
	    		a[j] = nextLong()+offset;
	    	return a;
		}
	}

	public static class MyPrintWriter extends PrintWriter{
		public MyPrintWriter(OutputStream os) {
			super(os);
		}

		public void printlnAns(long ans) {
			println(ans);
		}

		public void printlnAns(int ans) {
			println(ans);
		}

		public void printlnAns(boolean ans) {
			if(ans)
				println(YES);
			else
				println(NO);
		}

		public void printAns(long[] arr){
			if(arr != null && arr.length > 0){
				print(arr[0]);
				for(int i=1; i<arr.length; i++){
					print(" ");
					print(arr[i]);
				}
			}
		}
		public void printlnAns(long[] arr){
			printAns(arr);
			println();
		}

		public void printAns(int[] arr){
			if(arr != null && arr.length > 0){
				print(arr[0]);
				for(int i=1; i<arr.length; i++){
					print(" ");
					print(arr[i]);
				}
			}
		}
		public void printlnAns(int[] arr){
			printAns(arr);
			println();
		}

		public <T> void printAns(ArrayList<T> arr){
			if(arr != null && arr.size() > 0){
				print(arr.get(0));
				for(int i=1; i<arr.size(); i++){
					print(" ");
					print(arr.get(i));
				}
			}
		}
		public <T> void printlnAns(ArrayList<T> arr){
			printAns(arr);
			println();
		}

		public void printAns(int[] arr, int add){
			if(arr != null && arr.length > 0){
				print(arr[0]+add);
				for(int i=1; i<arr.length; i++){
					print(" ");
					print(arr[i]+add);
				}
			}
		}
		public void printlnAns(int[] arr, int add){
			printAns(arr, add);
			println();
		}

		public void printAns(ArrayList<Integer> arr, int add) {
			if(arr != null && arr.size() > 0){
				print(arr.get(0)+add);
				for(int i=1; i<arr.size(); i++){
					print(" ");
					print(arr.get(i)+add);
				}
			}
		}

		public void printlnAns(ArrayList<Integer> arr, int add){
			printAns(arr, add);
			println();
		}

		public void printlnAnsSplit(long[] arr, int split){
			if(arr != null){
				for(int i=0; i<arr.length; i+=split){
					print(arr[i]);
					for(int j=i+1; j<i+split; j++){
						print(" ");
						print(arr[j]);
					}
					println();
				}
			}
		}

		public void printlnAnsSplit(int[] arr, int split){
			if(arr != null){
				for(int i=0; i<arr.length; i+=split){
					print(arr[i]);
					for(int j=i+1; j<i+split; j++){
						print(" ");
						print(arr[j]);
					}
					println();
				}
			}
		}

		public <T> void printlnAnsSplit(ArrayList<T> arr, int split){
			if(arr != null && !arr.isEmpty()){
				for(int i=0; i<arr.size(); i+=split){
					print(arr.get(i));
					for(int j=i+1; j<i+split; j++){
						print(" ");
						print(arr.get(j));
					}
					println();
				}
			}
		}
	}

	static private void permutateAndSort(long[] a) {
		int n = a.length;
		Random R = new Random(System.currentTimeMillis());
		for(int i=0; i<n; i++) {
			int t = R.nextInt(n-i);
			long temp = a[n-1-i];
			a[n-1-i] = a[t];
			a[t] = temp;
		}
		Arrays.sort(a);
	}

	static private void permutateAndSort(int[] a) {
		int n = a.length;
		Random R = new Random(System.currentTimeMillis());
		for(int i=0; i<n; i++) {
			int t = R.nextInt(n-i);
			int temp = a[n-1-i];
			a[n-1-i] = a[t];
			a[t] = temp;
		}
		Arrays.sort(a);
	}

	static private int[][] constructChildren(int n, int[] parent, int parentRoot){
		int[][] childrens = new int[n][];

		int[] numChildren = new int[n];
		for(int i=0; i<parent.length; i++) {
			if(parent[i] != parentRoot)
				numChildren[parent[i]]++;
		}

		for(int i=0; i<n; i++) {
			childrens[i] = new int[numChildren[i]];
		}

		int[] idx = new int[n];
		for(int i=0; i<parent.length; i++) {
			if(parent[i] != parentRoot)
				childrens[parent[i]][idx[parent[i]]++] = i;
		}

		return childrens;
	}

	static private int[][][] constructDirectedNeighborhood(int n, int[][] e){
		int[] inDegree = new int[n];
		int[] outDegree = new int[n];
		for(int i=0; i<e.length; i++) {
			int u = e[i][0];
			int v = e[i][1];
			outDegree[u]++;
			inDegree[v]++;
		}

		int[][] inNeighbors = new int[n][];
		int[][] outNeighbors = new int[n][];
		for(int i=0; i<n; i++) {
			inNeighbors[i] = new int[inDegree[i]];
			outNeighbors[i] = new int[outDegree[i]];
		}

		int[] inIdx = new int[n];
		int[] outIdx = new int[n];
		for(int i=0; i<e.length; i++) {
			int u = e[i][0];
			int v = e[i][1];
			outNeighbors[u][outIdx[u]++] = v;
			inNeighbors[v][inIdx[v]++] = u;
		}

		return new int[][][] {inNeighbors, outNeighbors};
	}

	static private int[][] constructNeighborhood(int n, int[][] e) {
		int[] degree = new int[n];
		for(int i=0; i<e.length; i++) {
			int u = e[i][0];
			int v = e[i][1];
			degree[u]++;
			degree[v]++;
		}

		int[][] neighbors = new int[n][];
		for(int i=0; i<n; i++)
			neighbors[i] = new int[degree[i]];

		int[] idx = new int[n];
		for(int i=0; i<e.length; i++) {
			int u = e[i][0];
			int v = e[i][1];
			neighbors[u][idx[u]++] = v;
			neighbors[v][idx[v]++] = u;
		}

		return neighbors;
	}

	static private void drawGraph(int[][] e) {
		makeDotUndirected(e);
		try {
			final Process process = new ProcessBuilder("dot", "-Tpng", "graph.dot")
					.redirectOutput(new File("graph.png"))
					.start();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

	}

	static private void makeDotUndirected(int[][] e) {
		MyPrintWriter out2 = null;
		 try {
			out2 = new MyPrintWriter(new FileOutputStream("graph.dot"));
		} catch (FileNotFoundException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		out2.println("strict graph {");
		for(int i=0; i<e.length; i++){
			out2.println(e[i][0] + "--" + e[i][1] + ";");
		}
		out2.println("}");
		out2.close();
	}

	static private void makeDotDirected(int[][] e) {
		MyPrintWriter out2 = null;
		 try {
			out2 = new MyPrintWriter(new FileOutputStream("graph.dot"));
		} catch (FileNotFoundException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		out2.println("strict digraph {");
		for(int i=0; i<e.length; i++){
			out2.println(e[i][0] + "->" + e[i][1] + ";");
		}
		out2.println("}");
		out2.close();
	}

}


// practice with rainboy
// https://codeforces.com/blog/entry/58519
import java.io.*;
import java.util.*;

public class CF954H extends PrintWriter {
	CF954H() { super(System.out, true); }
	Scanner sc = new Scanner(System.in);
	public static void main(String[] $) {
		CF954H o = new CF954H(); o.main(); o.flush();
	}

	static final int MD = 1000000007, INV2 = 500000004;
	void main() {
		int n = sc.nextInt();
		int[] aa = new int[n - 1];
		for (int h = 0; h < n - 1; h++)
			aa[h] = sc.nextInt();
		int[][] pp = new int[n][n * 2];
		for (int h = 0; h < n; h++)
			pp[h][0] = 1;
		for (int k = 1; k < n; k++)
			for (int h = n - k - 1; h >= 0; h--)
				pp[h][k] = (int) ((long) aa[h] * pp[h + 1][k - 1] % MD);
		int[][] qq = new int[n][n * 2];
		for (int h = 0; h < n; h++)
			qq[h][0] = 1;
		for (int k = 1; k < n * 2; k++)
			for (int h = 1; h < n; h++) {
				long x = qq[h - 1][k - 1];
				if (k >= 2)
					x = (x + (long) (aa[h - 1] - 1) * pp[h][k - 2]) % MD;
				qq[h][k] = (int) x;
			}
		for (int k = 1; k < n * 2 - 1; k++) {
			long x = 0;
			for (int h = 0; h < n; h++)
				x = (x + (long) pp[0][h] * (pp[h][k] + qq[h][k])) % MD;
			print(x * INV2 % MD + " ");
		}
		println();
	}
}


import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.StringTokenizer;

public class Main {

    public static void main(String[] args) throws IOException {

        Scanner sc = new Scanner(System.in);
        PrintWriter out = new PrintWriter(System.out);

        int n = sc .nextInt();
        long[] a = new long[n];
        for(int i = 0; i < n; ++i)
            a[i] = sc.nextLong();
        ArrayList<Long> b = new ArrayList<>();
        ArrayList<Long> c = new ArrayList<>();
        ArrayList<Integer> d = new ArrayList<>();
        int idx = 0;
        long curPower = 1;
        for(long x: a)
            if (x == 1) {
                b.add(1l);
                d.add(1);
            } else if ((x & x - 1) == 0) {
                if (x != curPower) {
                    curPower = x;
                    idx = 0;
                }
                if (idx < b.size()) {
                    b.set(idx, x);
                    d.set(idx, d.get(idx) + 1);
                    ++idx;
                }
                else
                    c.add(x);
            } else {
                c.add(x);
            }
        int open = b.size();
        boolean possible = b.size() >= c.size();
        if(possible) for(int i = c.size() - 1, j = 0; i >= 0; --i, ++j) {
            if(c.get(i) <= 2 * b.get(j)) --open;
            else {
                possible = false;
                break;
            }
        }
        ArrayList<Integer> ans = new ArrayList<>();
        if (possible) {
            ans.add(b.size());
            int free = 0;
            for(int i = b.size() - 1; i >= 0; --i) {
                open--;
                free += d.get(i);
                if (open >= free)
                    ans.add(i);
                else
                    break;
            }
            Collections.reverse(ans);
            for(int x: ans)
                out.print(x + " ");
        } else {
            out.println(-1);
        }
        out.close();
    }

    static class Scanner
    {
        StringTokenizer st;
        BufferedReader br;

        public Scanner(InputStream s){	br = new BufferedReader(new InputStreamReader(s));}

        public String next() throws IOException
        {
            while (st == null || !st.hasMoreTokens())
                st = new StringTokenizer(br.readLine());
            return st.nextToken();
        }

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

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

        public String nextLine() throws IOException {return br.readLine();}

        public boolean ready() throws IOException {return br.ready();}

    }
}


//package educational.round68;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.InputMismatchException;

public class G3 {
	InputStream is;
	PrintWriter out;
//	String INPUT = "42";
	String INPUT = "";

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

	void solve()
	{
		BigInteger n = new BigInteger(ns());
		long ans = 0;
		for(int i = 1;i <= 9;i++){
			for(int j = 1;j <= i;j++){
				if(gcd(i, j) == 1){
					if(i == 1){
						ans += n.mod(BigInteger.valueOf(998244353L)).longValue();
					}else{
						long lans = count(n, i, j);
						if(i != j){
							ans += lans*2;
						}else{
							ans += lans;
						}
//						if(lans > 0)tr(i, j, lans, ans);
					}
				}
			}
		}
		out.println(ans % 998244353);
	}

	long count(BigInteger N, int u, int v)
	{
		char[] s = ("0" + N.divide(BigInteger.valueOf(u)).add(BigInteger.ONE).toString()).toCharArray();

		int n = s.length;
		// u:2 v:1
		// u倍したときにt*uを含んでいるか
		// v倍したときにt*vを含んでいるか
		int w = 9/u;
		long[][][][] dp = new long[1<<w][1<<w][10][10]; // valid carry
		long[][][][] ep = new long[1<<w][1<<w][10][10]; // valid carry
		int mod = 998244353;
		ep[0][0][0][0] = 1;

		int[][] va0 = new int[10][10];
		int[][] va1 = new int[10][10];
		int[][] nextc0 = new int[10][10];
		int[][] nextc1 = new int[10][10];
		for(int d = 0;d < 10;d++){
			for(int c0 = 0;c0 <= 9;c0++){
				int v0 = d*u + c0;
				for(int t = 1;t <= w;t++){
					if(t*u == v0%10){
						va0[d][c0] |= 1<<t-1;
					}
				}
				nextc0[d][c0] = v0 / 10;
			}
			for(int c1 = 0;c1 <= 9;c1++){
				int v1 = d*v + c1;
				for(int t = 1;t <= w;t++){
					if(t*v == v1%10){
						va1[d][c1] |= 1<<t-1;
					}
				}
				nextc1[d][c1] = v1 / 10;
			}
		}

		for(int i = n-1;i >= 0;i--){
			long[][][][] ndp = new long[1<<w][1<<w][10][10]; // valid carry
			long[][][][] nep = new long[1<<w][1<<w][10][10]; // valid carry

			for(int j = 0;j < 1<<w;j++){
				for(int k = 0;k < 1<<w;k++){
					for(int l = 0;l < 10;l++){
						for(int m = 0;m < 10;m++){
							int d = s[i]-'0';
							int nj = j|va0[d][l];
							int nk = k|va1[d][m];
							int nl = nextc0[d][l];
							int nm = nextc1[d][m];
							ndp[nj][nk][nl][nm] += dp[j][k][l][m];
							if(ndp[nj][nk][nl][nm] >= mod)ndp[nj][nk][nl][nm] -= mod;
						}
					}
				}
			}

			for(int j = 0;j < 1<<w;j++){
				for(int k = 0;k < 1<<w;k++){
					for(int l = 0;l < 10;l++){
						for(int m = 0;m < 10;m++){
							for(int d = 0;d < 10;d++){
								int nj = j|va0[d][l];
								int nk = k|va1[d][m];
								int nl = nextc0[d][l];
								int nm = nextc1[d][m];
								nep[nj][nk][nl][nm] += ep[j][k][l][m];
								if(nep[nj][nk][nl][nm] >= mod)nep[nj][nk][nl][nm] -= mod;
								if(d < s[i]-'0'){
									ndp[nj][nk][nl][nm] += ep[j][k][l][m];
									if(ndp[nj][nk][nl][nm] >= mod)ndp[nj][nk][nl][nm] -= mod;
								}
							}
						}
					}
				}
			}

			dp = ndp;
			ep = nep;
		}

		long ret = 0;
		for(int i = 0;i < 1<<w;i++){
			for(int j = 0;j < 1<<w;j++){
				if((i&j) != 0){
					ret += dp[i][j][0][0];
				}
			}
		}
		return ret % mod;
	}

	public static int gcd(int a, int b) {
		while (b > 0) {
			int c = a;
			a = b;
			b = c % b;
		}
		return a;
	}

	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 G3().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 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.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Queue;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.TreeSet;

public class Solution implements Runnable {

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

	BufferedReader in;
	PrintWriter out;
	StringTokenizer st;

	String nextToken() throws IOException {
		while (st == null || !st.hasMoreTokens()) {
			String r = in.readLine();
			if (r == null) return null;
			st = new StringTokenizer(r);
		}
		return st.nextToken();
	}

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

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

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

	class Edge {
		int b, e, n;
		Edge pr, nt, r;
		Edge(int bb, int ee, int nn, Edge rr, Edge tt) {
			b = bb; e = ee; n = nn; pr = rr; nt = tt;
			r = null;
		}
	}

	Edge[] v;
	Edge cycleEdge;
	int[] ss;

	void addedge(int b, int e, int n) {
		Edge r = new Edge(b, e, n, null, v[b]);
		if (v[b] == null) v[b] = r; else v[b] = v[b].pr = r;
		r = new Edge(e, b, n, null, v[e]);
		if (v[e] == null) v[e] = r; else v[e] = v[e].pr = r;
		v[b].r = v[e]; v[e].r = v[b];
		if (get(b) != get(e)) uni(b, e); else cycleEdge = v[e];
		ss[b]++; ss[e]++;
	}

	int[] dsu;
	int[] rank;
	Queue<Integer> cc, cp, anss;

	int get(int q) {
		if (dsu[q] != q) return dsu[q] = get(dsu[q]); else return q;
	}

	void uni(int a, int b) {
		a = get(a); b = get(b);
		if (rank[a] == rank[b]) rank[a]++;
		if (rank[a] > rank[b]) dsu[b] = a; else dsu[a] = b;
	}

	int[] w;
	boolean[] ww;

	void ansprint() {
		out.print(anss.size() + " ");
		while (!anss.isEmpty()) out.print(anss.poll() + " ");
		out.println();
	}

	void changeEdge(Edge r, int b, int e) {
		if (r.b != b) changeEdge(r.r, b, e); else {
			ansprint();
			out.println(r.n + " " + b + " " + e);
			r.e = e;
			if (r.r.pr == null) {
				v[r.r.b] = r.r.nt;
			} else {
				r.r.pr.nt = r.r.nt;
			}
			if (r.r.nt != null) r.r.nt.pr = r.r.pr;
			r.r.b = b;
			r.r.nt = v[b];
			r.r.pr = null;
			v[b] = v[b].pr =  r.r;
		}
	}

	void dfs(int q, int rr) {
		w[q] = 1;
		ww[get(q)] = true;
		anss.add(q);
		Queue<Edge> obr = new LinkedList<Edge>();
		for (Edge r = v[q]; r != null; r = r.nt) {
			if (r.n != rr) {
				if (w[r.e] == 0) {
					dfs(r.e, r.n);
					anss.add(q);
				}
				obr.add(r);
			}
		}
		while (!obr.isEmpty()) {
			int qq = -1;
			while (!cc.isEmpty() && ww[cc.peek()]) cc.poll();
			if (cc.isEmpty()) {
				while (!cp.isEmpty() && ww[cp.peek()]) cp.poll();
				if (!cp.isEmpty()) qq = cp.poll();
			} else qq = cc.poll();
			if (qq == -1) break;
			Edge r = obr.poll();
			changeEdge(r, q, qq);
			dfs(qq, r.n);
			anss.add(q);
		}
		w[q] = 2;
	}

	void solve() throws Exception {
		int n = nextInt();
		int m = nextInt();
		v = new Edge[n + 1];
		dsu = new int[n + 1];
		rank = new int[n + 1];
		ss = new int[n + 1];
		for (int i = 1; i <= n; i++) dsu[i] = i;
		for (int i = 1; i <= m; i++) {
			addedge(nextInt(), nextInt(), i);
		}
		cc = new LinkedList<Integer>();
		cp = new LinkedList<Integer>();
		int cs = 0;
		for (int i = 1; i <= n; i++)
			if (dsu[i] == i && i != get(1)) {
				cs++;
				if (rank[i] == 0) cp.add(i); else cc.add(i);
			}
		if (cs > m || cs == m && cycleEdge == null && ss[1] == 0) {
			out.println("NO");
		} else {
			out.println("YES");
			ww = new boolean[n + 1];
			w = new int[n + 1];
			anss = new LinkedList<Integer>();
			if (ss[1] == 0) {
				anss.add(1);
				if (cycleEdge != null) {
					changeEdge(cycleEdge, 1, cycleEdge.e);
				} else {
					int j = 2;
					while (ss[j] != 1) j++;
					changeEdge(v[j], 1, v[j].e);
					cp.add(j);
					cs++;
				}
			}
			out.println(cs);
			dfs(1, 0);
			ansprint();
		}
	}

	public void run() {
		Locale.setDefault(Locale.UK);
		try {
			in = new BufferedReader(new InputStreamReader(System.in));
			out = new PrintWriter(System.out);
			solve();
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(1);
		} finally {
			out.flush();
		}
	}

}


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

public class F946{

    long[] fibpow;
    char[] chs;
    long mod = 1000000007L;
    long modpow = mod-1L;
    long[][][] memo;

    long pow(long m, long n){
        long res = 1;
        while(n > 0){
            if(n % 2 == 1)res = (res*m)%mod;
            m = (m*m)%mod;
            n = n/2;
        }
        return res;
    }

    private void solve() throws Exception {
        int n = nextInt();
        int x = nextInt();
        chs = nextToken().toCharArray();
        memo = new long[n][n][x+1];
        for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) Arrays.fill(memo[i][j], -1);
        fibpow = new long[x+1];
        if(x > 0) fibpow[1] = 1L;
        for(int i = 2; i <= x; i++) fibpow[i] = (fibpow[i-1]+ fibpow[i-2])%modpow;
        for(int i = 0; i <= x; i++) fibpow[i] = pow(2, fibpow[i]);
        out.println(doit(0,n-1,x));
    }

    long doit(int i, int j, int x) {
        if (x < 2 && i == j && chs[i]-'0' == x) return 1L;
        else if (x < 2 && i == j && chs[i]-'0' != x) return 0L;
        else if(x < 2 && i < j) return 0L;
        else if(memo[i][j][x] < 0){
            long res = 0L;
            res = (res + ((i==0?fibpow[x]:(1L)) * doit(i,j, x-2))%mod)%mod;
            for(int t = i; t < j; t++) res = (res + (doit(i,t,x-1) * doit(t+1,j, x-2))%mod)%mod;
            res = (res + (doit(i,j,x-1) * (j==chs.length-1? fibpow[x-1]:(1L)))%mod)%mod;
            memo[i][j][x] = res;
        }
        return memo[i][j][x];
    }

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

    private BufferedReader in;
    private PrintWriter out;
    private StringTokenizer tokenizer;

    public void run() {
        try {
            in = new BufferedReader(new InputStreamReader(System.in));
            tokenizer = null;
            out = new PrintWriter(System.out);
            solve();
            in.close();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

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

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

    private float nextFloat() throws IOException {
        return Float.parseFloat(nextToken());
    }

    private String nextLine() throws IOException {
        return new String(in.readLine());
    }

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

}


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.StringTokenizer;
/*
4
1 2 3 4
2 1 4 3
1 2 3 4
3 1 4 2
>> 3

1 2 3 4
2 1 4 3
1 2 3 4
2 1 4 3

1 2 3 4
2 1 4 3
1 2 3 4
2 3 4 1

1 2 3 4
2 1 4 3
1 2 3 4
2 4 1 3

1 2 3 4
2 1 4 3
1 2 3 4
3 1 4 2
 */
public class E3 {
	static PrintWriter out = new PrintWriter(System.out);

	static int MAX = 2005;
	static long mod = 998_244_353L;
	static long fact[];
	static long dp[][];

	static int N;
	static int mat[][];
	public static void main(String[] args) {
		FS in = new FS();
		fact = new long[MAX];
		fact[0] = 1;
		for(int i = 1; i < MAX; i++) fact[i] = (fact[i-1]*i)%mod;
		dp = new long[MAX][MAX];
		for(long a[] : dp) Arrays.fill(a, -1);

		N = in.nextInt();
		mat = new int[N][N];
		for(int i = 0; i < N; i++)
			for(int j = 0; j < N; j++)
				mat[i][j] = in.nextInt()-1;

		long whenFree = go(N, N);
		long freePow[] = new long[N+1];
		freePow[0] = 1;
		for(int i = 1; i <= N; i++) {
			freePow[i] = (whenFree * freePow[i-1])%mod;
		}

//		for(int i = 1; i <= N; i++) {
//			for(int j = 0; j <= i; j++) {
//				System.out.println("n="+i+" ab="+j+" res = "+go(i,j));
//			}
//		}
//
//		System.out.println("FP = "+whenFree);

		long res = 0;
		for(int i = 0; i < N; i++) {
			int sz = N;
			BIT have = new BIT(N);
			BIT both = new BIT(N);
			for(int j = 0; j < N; j++) have.update(j, 1);
			for(int j = 0; j < N; j++) both.update(j, 1);

			boolean usedCur[] = new boolean[N];
			boolean usedPrev[] = new boolean[N];

			int numEq = N;

			for(int j = 0; j < N; j++) {
				sz--;
				// place something less than it
				long areLess = have.sum(0, mat[i][j]-1);
				if(i > 0 && mat[i-1][j] < mat[i][j]) areLess -= have.sum(mat[i-1][j], mat[i-1][j]);

				long lessAndBoth = i == 0 ? 0 : both.sum(0, mat[i][j]-1);
				if(i > 0 && mat[i-1][j] < mat[i][j]) lessAndBoth -= both.sum(mat[i-1][j], mat[i-1][j]);

				long willDeqEq = lessAndBoth;
				long willNotDeqEq = areLess - lessAndBoth;

				int subAgain = i > 0 && !usedCur[mat[i-1][j]] ? 1 : 0;
				// Now see if what we are placing will dec numEq or not

				res = add(res, mult(freePow[N-i-1], mult(willNotDeqEq, go(sz, (i == 0 ? 0 : numEq-subAgain)))));
				res = add(res, mult(freePow[N-i-1], mult(willDeqEq, go(sz, (i == 0 ? 0 : numEq-1-subAgain)))));

//				System.out.println(i+" "+j+"  less = "+areLess+" sz = "+sz+"  res = "+res+" Eq = "+numEq+"  deq = "+willDeqEq+"  not = "+willNotDeqEq);

				// place the current element and continue
				have.update(mat[i][j], -1);
				// subtract 1 from numEq if this is the first
				// time removing a number
				if(!usedPrev[mat[i][j]]) {
					numEq--;
					both.update(mat[i][j], -1);
				}
				if(i > 0 && !usedCur[mat[i-1][j]]) {
					numEq--;
					both.update(mat[i-1][j], -1);
				}

				if(i > 0) usedPrev[mat[i-1][j]] = true;
				usedCur[mat[i][j]] = true;
			}
		}
		out.println(res);
		out.close();
	}

	static long go(int n, int above) {
		if(n <= 0 || above < 0) return 0;
		if(n == 1) return above == 1 ? 0 : 1;
		if(above == 0) return fact[n];
		if(dp[n][above] != -1) return dp[n][above];
		long free = n-above;
		long notFree = above-1;
		long res = 0;
		res += (free * go(n-1, above-1));
		res += (notFree * go(n-1, above-2));
		return dp[n][above] = res%mod;
	}

	static long add(long a, long b) {
		long x = a+b;
		return x >= mod ? x-mod : x;
	}
	static long mult(long a, long b) {
		long x = a*b;
		return x >= mod ? x%mod : x;
	}

	static class BIT {
		int n;
		int[] tree;

		public BIT(int n) {
			this.n = n;
			tree = new int[n + 2];
		}

		int read(int i) {
			i++;
			int sum = 0;
			while (i > 0) {
				sum += tree[i];
				i -= i & -i;
			}
			return sum;
		}

		int sum(int a, int b) {
			if(a > b) return 0;
			return read(b) - (a == 0 ? 0 : read(a-1));
		}

		void update(int i, int val) {
			i++;
			while (i <= n) {
				tree[i] += val;
				i += i & -i;
			}
		}

		// if the BIT is a freq array, returns the
		// index of the kth item, or n if there are fewer
		// than k items.
		int getKth(int k) {
			int e=Integer.highestOneBit(n), o=0;
			for (; e!=0; e>>=1) {
				if (e+o<=n && tree[e+o]<=k) {
					k-=tree[e+o];
					o+=e;
				}
			}
			return o;
		}

	}

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

}


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

public class Main {

    private static myScanner sc;
    private static PrintWriter pw;

    private static final boolean defaultInAndOutPut = true;

    private static final int numberOfTests = 1;

    private static final String nameOfInAndOutFile = "";
    private static final String fullNameInputFile = nameOfInAndOutFile + ".in";
    private static final String fullNameOutputFile = nameOfInAndOutFile + ".out";

    public static void main(String[] args) throws IOException {
        if (defaultInAndOutPut) {
            setDefaultInAndOutPut();
        } else {
            setInAndOutPutFromFile();
        }
        for (int test = 1; test <= numberOfTests; test++) {
            solve();
            pw.flush();
        }

        pw.close();
    }

    private static void setDefaultInAndOutPut() {
        sc = new myScanner(new BufferedReader(new InputStreamReader(System.in)));
        pw = new PrintWriter(System.out);
    }

    private static void setInAndOutPutFromFile() throws IOException {
        sc = new myScanner(new BufferedReader(new FileReader(new File(fullNameInputFile))));
        pw = new PrintWriter(new File(fullNameOutputFile));
    }

    private static final int intINF = Integer.MAX_VALUE;
    private static final long longINF = Long.MAX_VALUE;

    private static void solve() {
        int n = sc.nextInt();
        long k =sc.nextLong();
        long[] a = new long[n];
        TreeSet<Long> set = new TreeSet<>();
        for (int i=0;i<n;i++){
            k+=a[i]=sc.nextInt();
            for (long j=1;j*j<=a[i];j++){
                set.add(j);
                set.add((a[i]+j-1)/j);
            }
        }
        long ans = 0;
        for (Long anArr : set) {
            long cur = 0;
            for (int i = 0; i < n; i++) cur += (a[i]+anArr-1)/anArr;
            long d = k / cur;
            if (anArr <= d && ans < d) ans = d;
        }
        pw.print(ans);
    }
}

class myScanner {
    private char[] buffer = new char[1 << 8];
    private int pos = 1;

    private BufferedReader reader;

    public myScanner(BufferedReader reader) {
        this.reader = reader;
    }

    public boolean hasNext() {
        return pos > 0;
    }

    private void loadBuffer() {
        pos = 0;
        try {
            for (int i; (i = reader.read()) != -1; ) {
                char c = (char) i;
                if (c != ' ' && c != '\n' && c != '\t' && c != '\r' && c != '\f') {
                    if (pos == buffer.length) buffer = Arrays.copyOf(buffer, 2 * pos);
                    buffer[pos++] = c;
                } else if (pos != 0) break;
            }
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    public String current() {
        return String.copyValueOf(buffer, 0, pos);
    }

    public String next() {
        loadBuffer();
        return current();
    }

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

    public int nextInt() {
        return nextInt(10);
    }

    public long nextLong() {
        return nextLong(10);
    }

    public int nextInt(int radix) {
        loadBuffer();
        int result = 0;
        int i = buffer[0] == '-' || buffer[0] == '+' ? 1 : 0;
        for (checkValidNumber(pos > i); i < pos; i++) {
            int digit = buffer[i] - '0';
            checkValidNumber(0 <= digit && digit <= radix - 1);
            result = result * radix + digit;
        }
        return buffer[0] == '-' ? -result : result;
    }

    public long nextLong(int radix) {
        loadBuffer();
        long result = 0;
        int i = buffer[0] == '-' || buffer[0] == '+' ? 1 : 0;
        for (checkValidNumber(pos > i); i < pos; i++) {
            int digit = buffer[i] - '0';
            checkValidNumber(0 <= digit && digit <= radix - 1);
            result = result * radix + digit;
        }
        return buffer[0] == '-' ? -result : result;
    }

    public int[] nextArrayInts(int size) {
        int[] input = new int[size];
        for (int i = 0; i < size; i++) input[i] = nextInt();
        return input;
    }

    public long[] nextArrayLongs(int size) {
        long[] input = new long[size];
        for (int i = 0; i < size; i++) input[i] = nextLong();
        return input;
    }

    public double nextDouble() {
        loadBuffer();
        long result = 0;
        int i = buffer[0] == '-' || buffer[0] == '+' ? 1 : 0;
        long round = 1;
        final int radix = 10;
        boolean hasPoint = false;
        for (checkValidNumber(pos > i); i < pos; i++) {
            int digit = buffer[i] - '0';
            checkValidNumber((0 <= digit && digit <= radix - 1) || (!hasPoint && digit == -2));
            if (digit == -2) hasPoint = true;
            else {
                if (hasPoint) round *= radix;
                result = result * radix + digit;
            }

        }
        return buffer[0] == '-' ? -result / (1.0 * round) : result / (1.0 * round);
    }

    private void checkValidNumber(boolean condition) {
        if (!condition) throw new NumberFormatException(current());
    }
}


//package prac;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.InputMismatchException;

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

	int[] a;

	/*
[[0, 9777, 29196, 45342, 70884, 89350, 107220, 132692, 151648, 170604, 189560]]
[[0, 9777, 24375, 40005, 62940, 81082, 97682, 121166, 140122, 159078, 178034]]
[[0, 9777, 24375, 39489, 57726, 75745, 89414, 115829, 133894, 153741, 169269]]
	 */
	void solve()
	{
		int n = ni(), K = ni();
		a = na(n);

		long[] dp = new long[n+1];
		Arrays.fill(dp, Long.MAX_VALUE / 100000);
		dp[0] = 0;
		for(int i = 1;i <= K;i++) {
			dp = go(dp);
		}
		out.println(dp[n]);
	}

	long[] go(long[] dp)
	{
		int n = dp.length-1;
		long[] ret = new long[n+1];
		int[] stack = new int[n+1];
		int[] times = new int[n+1];
		LiChaoTreeWithNode[] lcts = new LiChaoTreeWithNode[n+1];
		PersistentLiChaoTree plct = new PersistentLiChaoTree(n+1, 0, 17);
		int sp = 0;

		// max * (i-j) + dp[j]
		// max * i + max*(-j) + dp[j]
		for(int i = 0;i < n;i++) {
			lcts[i] = new LiChaoTreeWithNode(0, 17);
			lcts[i].add(new LiChaoTreeWithNode.Node(-i, dp[i]));

			while(sp > 0 && a[i] > a[stack[sp-1]]){
				lcts[i] = merge(lcts[i], lcts[stack[sp-1]]);
				lcts[stack[sp-1]] = null;
				sp--;
			}
			// x+4

			plct.add(sp == 0 ? 0 : times[sp-1], a[i], lcts[i].f(a[i]));
			stack[sp] = i;
			times[sp] = plct.last();
			sp++;
			ret[i+1] = plct.f(plct.last(), i+1);
		}
		return ret;
	}

	LiChaoTreeWithNode merge(LiChaoTreeWithNode a, LiChaoTreeWithNode b)
	{
		if(b.num > a.num) {
			LiChaoTreeWithNode d = a; a = b; b = d;
		}
		adds(b.root, a);
		return a;
	}

	void adds(LiChaoTreeWithNode.Node x, LiChaoTreeWithNode a)
	{
		if(x == null)return;
		a.add(new LiChaoTreeWithNode.Node(x.a, x.b));
		adds(x.left, a);
		adds(x.right, a);
	}

	public static class LiChaoTreeWithNode {

		public static class Node
		{
			public long a, b;
			public Node left, right;

			public long f(long x)
			{
				return a*x+b;
			}

			public Node(long a, long b) {
				this.a = a;
				this.b = b;
			}

			@Override
			public String toString() {
				return "Node [a=" + a + ", b=" + b + ", " + (left != null ? "left=" + left + ", " : "")
						+ (right != null ? "right=" + right : "") + "]";
			}
		}

		public long L, D;
		public Node root;
		public int num;

		// [L, L+(1<<D))
		public LiChaoTreeWithNode(long L, int D)
		{
			root = null;
			this.L = L;
			this.D = D;
			num = 0;
		}

		// params
		public boolean add(Node x)
		{
			num++;
			long l = L, r = L+(1L<<D);
			Node cur = root;
			Node par = null;
			boolean ret = false;
			int lr = 0;
			// [l,r)
			while(cur != null){
				boolean lessl = x.f(l) < cur.f(l);
				boolean lessr = x.f(r-1) < cur.f(r-1);
				if(!lessl && !lessr){
					// not need to append
					return ret;
				}
				if(lessl && lessr){
					// replace
					if(par != null){
						if(par.left == cur){
							par.left = x;
						}else{
							par.right = x;
						}
					}
					x.left = cur.left;
					x.right = cur.right;
					cur.left = cur.right = null;
					if(root == cur)root = x;
					return true;
				}
//				U.tr("come", l, r, x, cur);
				long m = l + (r-l)/2;
				boolean lessm = x.f(m) < cur.f(m);
				if(lessm){
					// swap
					if(par != null){
						if(par.left == cur){
							par.left = x;
						}else{
							par.right = x;
						}
					}
					x.left = cur.left;
					x.right = cur.right;
					cur.left = cur.right = null;
					if(root == cur)root = x;
					Node d = cur; cur = x; x = d;
					ret = true;
					lessl = !lessl;
					lessr = !lessr;
				}
//				U.tr("come2", l, r, x, cur);
				if(lessl){
					r = m;
					par = cur;
					lr = 0;
					cur = cur.left;
				}else{
					l = m;
					par = cur;
					lr = 1;
					cur = cur.right;
				}
			}
			if(par != null){
				if(lr == 0){
					par.left = x;
				}else{
					par.right = x;
				}
			}else{
				root = x;
			}
			return ret;
		}

		public long f(long x)
		{
			assert L <= x;
			assert x < L+(1L<<D);
			long min = Long.MAX_VALUE;
			Node cur = root;
			for(long m = D-1;m >= 0 && cur != null;m--){
				min = Math.min(min, cur.f(x));
				if(x-L<<~m<0){
					cur = cur.right;
				}else{
					cur = cur.left;
				}
			}
			return min;
		}
	}

	public static int lowerBound(int[] a, int v){ return lowerBound(a, 0, a.length, v); }
	public static int lowerBound(int[] a, int l, int r, int v)
	{
		if(l > r || l < 0 || r > a.length)throw new IllegalArgumentException();
		int low = l-1, high = r;
		while(high-low > 1){
			int h = high+low>>>1;
			if(a[h] >= v){
				high = h;
			}else{
				low = h;
			}
		}
		return high;
	}

	public static class PersistentLiChaoTree {

		// parameters of function
		public long[] as;
		public long[] bs;

		public int gen;
		public long L, D;
		public int[] lefts, rights;

		public int[] entries;
		public int egen;

		// [L, L+(1<<D))
		public PersistentLiChaoTree(int Q, long L, int D)
		{
			as = new long[Q*(D+1)*2+1];
			bs = new long[Q*(D+1)*2+1];
			lefts = new int[Q*(D+1)*2+1];
			rights = new int[Q*(D+1)*2+1];
			Arrays.fill(lefts, -1);
			Arrays.fill(rights, -1);

			entries = new int[Q+1];
			Arrays.fill(entries, -1);
			egen = 0;

			this.L = L;
			this.D = D;

			gen = 0;
			as[gen] = 0; bs[gen] = Long.MAX_VALUE; // dummy
			gen++;
			entries[egen++] = 0;
		}

		public int last() { return egen-1; }

		public int clone(int id)
		{
			if(id == -1)return -1;
			as[gen] = as[id];
			bs[gen] = bs[id];
			lefts[gen] = lefts[id];
			rights[gen] = rights[id];
			return gen++;
		}

		// add ax+b
		public void add(int eid, long a, long b)
		{
			assert 0 <= eid && eid < egen;
			long l = L, r = L+(1L<<D);
			int cur = entries[eid], par = -1, lr = -1;
			// [l,r)
			entries[egen++] = cur = clone(cur);

			while(cur != -1){
				boolean lessl = f(l, a, b) < f(l, as[cur], bs[cur]);
				boolean lessr = f(r-1, a, b) < f(r-1, as[cur], bs[cur]);
				if(!lessl && !lessr){
					// not need to append
					return;
				}
				if(lessl && lessr){
					// replace
					as[cur] = a; bs[cur] = b; return;
				}
				long m = l + (r-l)/2;
				boolean lessm = f(m, a, b) < f(m, as[cur], bs[cur]);
				if(lessm){
					// swap
					long da = as[cur], db = bs[cur];
					as[cur] = a; bs[cur] = b;
					a = da; b = db;
					lessl = !lessl;
					lessr = !lessr;
				}
				if(lessl){
					r = m;
					par = cur;
					lr = 0;
					lefts[cur] = clone(lefts[cur]);
					cur = lefts[cur];
				}else{
					l = m;
					par = cur;
					lr = 1;
					rights[cur] = clone(rights[cur]);
					cur = rights[cur];
				}
			}
			if(par != -1){
				if(lr == 0){
					lefts[par] = gen;
				}else{
					rights[par] = gen;
				}
			}
			as[gen] = a; bs[gen] = b;
			gen++;
		}

		public long f(int eid, long x)
		{
			assert 0 <= eid && eid < egen;
			assert L <= x;
			assert x < L+(1L<<D);
			int cur = entries[eid];
			long min = Long.MAX_VALUE;
			for(long m = D;m >= 0 && cur != -1;m--){
				min = Math.min(min, f(x, as[cur], bs[cur]));
				if(m == 0)break;
				if(x-L<<~(m-1)<0){
					cur = rights[cur];
				}else{
					cur = lefts[cur];
				}
			}
			return min;
		}

		public static long f(long x, long a, long b)
		{
			return a*x+b;
		}
	}

	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 ECR66G2().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 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.InputStreamReader;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
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);
		PrintWriter out = new PrintWriter(outputStream);
		TaskD solver = new TaskD();
		solver.solve(1, in, out);
		out.close();
	}
}

class TaskD {
    static class Point {
        int x;
        int y;
        int z;

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

        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Point point = (Point) o;

            if (x != point.x) return false;
            if (y != point.y) return false;
            if (z != point.z) return false;

            return true;
        }

        public int hashCode() {
            int result = x;
            result = 31 * result + y;
            result = 31 * result + z;
            return result;
        }
    }

	public void solve(int testNumber, InputReader in, PrintWriter out) {
        Point[][] ans = new Point[51][];
        ans[1] = new Point[]{new Point(0, 0, 0)};
        ans[2] = new Point[]{new Point(0, 0, 0), new Point(0, 1, 0), new Point(1, 1, 0), new Point(1, 0, 0), new Point(1, 0, 1), new Point(1, 1, 1), new Point(0, 1, 1), new Point(0, 0, 1)};
        for (int n = 3; n < ans.length; ++n) {
            Point[] prev = ans[n - 1];
            int prevSize = (n - 1) * (n - 1) * (n - 1);
            if (prev.length != prevSize) throw new RuntimeException();
            int sideSize = (n - 1) * (n - 1);
            if (!prev[prevSize - sideSize - 1].equals(new Point(n - 2, 0, n - 3))) throw new RuntimeException();
            if (!prev[prevSize - sideSize].equals(new Point(n - 2, 0, n - 2))) throw new RuntimeException();
            if (!prev[prevSize - 1].equals(new Point(0, 0, n - 2))) throw new RuntimeException();
            if (!prev[0].equals(new Point(0, 0, 0))) throw new RuntimeException();
            Point[] cur = new Point[n * n * n];
            ans[n] = cur;
            int curPtr = 0;
            for (int i = 0; i < sideSize; ++i) {
                Point old = prev[prevSize - 1 - i];
                cur[curPtr++] = new Point(old.x, old.y, 0);
            }
            for (int i = 0; i < prevSize; ++i) {
                Point old = prev[i];
                cur[curPtr++] = new Point(n - 2 - old.x, old.y, old.z + 1);
            }
            Point[] rect = getRectangleTraversal(n - 1, n);
            for (int i = 0; i < rect.length; ++i) {
                cur[curPtr++] = new Point(n - 1, rect[i].x, rect[i].y);
            }
            rect = getRectangleTraversal2(n);
            for (int i = 0; i < rect.length; ++i) {
                cur[curPtr++] = new Point(rect[i].x, n - 1, rect[i].y);
            }
            if (curPtr != cur.length) throw new RuntimeException();
            for (int i = 0; i < cur.length; ++i) {
                int tmp = cur[i].y;
                cur[i].y = cur[i].z;
                cur[i].z = tmp;
            }
        }
        int n = in.nextInt();
        int[][][] field = new int[n][n][n];
        Point prev = null;
        int at = 0;
        for (Point p : ans[n]) {
            if (prev != null) {
                int d = Math.abs(p.x - prev.x) + Math.abs(p.y - prev.y) + Math.abs(p.z - prev.z);
                if (d != 1) throw new RuntimeException();
            }
            prev = p;
            ++at;
            if (field[p.x][p.y][p.z] != 0) throw new RuntimeException();
            field[p.x][p.y][p.z] = at;
        }
        boolean xf = true;
        for (int[][] x : field) {
            if (xf) xf = false; else out.println();
            for (int[] y : x) {
                boolean zf = true;
                for (int z : y) {
                    if (zf) zf = false; else out.print(" ");
                    if (z == 0) throw new RuntimeException();
                    out.print(z);
                }
                out.println();
            }
        }
	}

    private Point[] getRectangleTraversal2(int n) {
        int sx = n;
        int sy = n;
        Point[] res = new Point[sx * sy];
        int resPtr = 0;
        if (sy % 2 == 0) {
            for (int y = sy - 1; y >= 0; --y) {
                if (y % 2 != 0) {
                    for (int x = 0; x < sx; ++x)
                        res[resPtr++] = new Point(y, x, 0);
                } else {
                    for (int x = sx - 1; x >= 0; --x)
                        res[resPtr++] = new Point(y, x, 0);
                }
            }
        } else {
            for (int y = sy - 1; y >= 2; --y) {
                if (y % 2 == 0) {
                    for (int x = 0; x < sx; ++x)
                        res[resPtr++] = new Point(y, x, 0);
                } else {
                    for (int x = sx - 1; x >= 0; --x)
                        res[resPtr++] = new Point(y, x, 0);
                }
            }
            for (int x = sx - 1; x >= 0; --x) {
                if (x % 2 == 0) {
                    res[resPtr++] = new Point(1, x, 0);
                    res[resPtr++] = new Point(0, x, 0);
                } else {
                    res[resPtr++] = new Point(0, x, 0);
                    res[resPtr++] = new Point(1, x, 0);
                }
            }
        }
        if (resPtr != res.length) throw new RuntimeException();
        return res;
    }

    private Point[] getRectangleTraversal(int sx, int sy) {
        Point[] res = new Point[sx * sy];
        int resPtr = 0;
        if (sy % 2 != 0) {
            for (int y = sy - 1; y >= 0; --y) {
                if (y % 2 == 0) {
                    for (int x = 0; x < sx; ++x)
                        res[resPtr++] = new Point(x, y, 0);
                } else {
                    for (int x = sx - 1; x >= 0; --x)
                        res[resPtr++] = new Point(x, y, 0);
                }
            }
        } else {
            for (int y = sy - 1; y >= 2; --y) {
                if (y % 2 != 0) {
                    for (int x = 0; x < sx; ++x)
                        res[resPtr++] = new Point(x, y, 0);
                } else {
                    for (int x = sx - 1; x >= 0; --x)
                        res[resPtr++] = new Point(x, y, 0);
                }
            }
            for (int x = 0; x < sx; ++x) {
                if (x % 2 == 0) {
                    res[resPtr++] = new Point(x, 1, 0);
                    res[resPtr++] = new Point(x, 0, 0);
                } else {
                    res[resPtr++] = new Point(x, 0, 0);
                    res[resPtr++] = new Point(x, 1, 0);
                }
            }
        }
        if (resPtr != res.length) throw new RuntimeException();
        return res;
    }
}

class InputReader {
    private BufferedReader reader;
    private StringTokenizer tokenizer;

    public InputReader(InputStream stream) {
        reader = new BufferedReader(new InputStreamReader(stream));
        tokenizer = null;
    }

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

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

    }


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;

public class CF1528E {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    StreamTokenizer st = new StreamTokenizer(br);

    final int N = (int) (1e6 + 10);
    final int M = 998244353;

    long[] dp = new long[N], dp1 = new long[N], dp2 = new long[N];

    private long readLong() throws IOException {
        st.nextToken();
        return (long) st.nval;
    }

    private int readInt() throws IOException {
        st.nextToken();
        return (int) st.nval;
    }

    private String readString() throws Exception {
        st.nextToken();
        return st.sval;
    }

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

    long inv(long a) {
        return inv(a, M);
    }

    public long inv(long a, long mod) {
        long b = mod;
        long p = 1, q = 0;
        while (b > 0) {
            long c = a / b;
            long d;
            d = a;
            a = b;
            b = d % b;
            d = p;
            p = q;
            q = d - c * q;
        }
        return p < 0 ? p + mod : p;
    }

    long g(int n) {
        if (n < 0)
            return 0;
        long t = dp[n];
        long res = (t * (t + 1)) % M;
        res = (res * (t + 5)) % M;
        res = (res * inv(3)) % M;
        return (res + M) % M;
    }

    private void run() throws Exception {
        int n = readInt();
        dp[0] = dp1[0] = dp2[0] = 1;

        for (int i = 1; i <= n; i++) {
            dp[i] = ((dp[i - 1] + 1) * (dp[i - 1] + 2) / 2) % M;
            dp1[i] = dp[i] - dp[i - 1];
            dp2[i] = dp1[i] - dp1[i - 1];
        }
        long res = (g(n - 1) - g(n - 2)) % M;
        for (int i = 0; i < n; i++)
            res = (res + dp2[i] * dp1[n - 1 - i]) % M;

        System.out.println(res);
    }
}


import java.util.Scanner;

public class Polykarp {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		int m = in.nextInt();
		int k = in.nextInt();
		int a = in.nextInt() - 1;
		int b = in.nextInt() - 1;

		int ap = a/(m*k);
		int ae = (a%(m*k)) / k;

		int bp = b/(m*k);
		int be = (b%(m*k)) / k;

		int t = 0;

		if (ap == bp) {
			if (ae != be) {
				t = min(abs(ae - be)*5, 10 + abs(ae - be));
			}
		}
		else {
			t += min(ae*5, 10 + ae);
			t += min(mod(ap - bp, n), mod(bp - ap, n)) * 15;
			t += min(be*5, be + 10);
		}

		System.out.print(t);
		in.close();
	}

	public static int min(int a, int b) {
		if (a < b) {
			return a;
		}
		else {
			return b;
		}
	}

	public static int abs(int a) {
		if (a >=0) {
			return a;
		}
		else {
			return -a;
		}
	}

	public static int mod(int a, int n) {
		while (a < 0) {
			a += n;
		}
		return a%n;
	}
}