//package educational.round31;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.InputMismatchException;
import java.util.List;

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

	void solve()
	{
		int n = ni();
		char[] s = ns(n);

		int[] f = new int[26];
		for(char c : s)f[c-'a']++;
		for(int i = 0;i < 26;i++){
			if(f[i] > n/2){
				out.println(-1);
				return;
			}
		}

		int[] a = na(n);
		for(int i = 0;i < n/2;i++){
			if(a[i] > a[n-1-i]){
				int d = a[i]; a[i] = a[n-1-i]; a[n-1-i] = d;
				char u = s[i]; s[i] = s[n-1-i]; s[n-1-i] = u;
			}
		}
		List<Edge> es = new ArrayList<>();
		int src = n, sink = n+1;
		for(int i = 0;i < n/2;i++){
			es.add(new Edge(src, i, 1, 0));
			es.add(new Edge(n-1-i, sink, 1, 0));
			for(int j = 0;j < n/2;j++){
				if(s[i] != s[n-1-j]){
					es.add(new Edge(i, n-1-j, 1, i == j ? 0 : a[i]));
				}
			}
		}
		long ans = Arrays.stream(a).sum();
		out.println(ans - solveMinCostFlow(compileWD(sink+1, es), src, sink, n));
	}

	public static class Edge
	{
		public int from, to;
		public int capacity;
		public int cost;
		public int flow;
		public Edge complement;
		// public int iniflow;

		public Edge(int from, int to, int capacity, int cost) {
			this.from = from;
			this.to = to;
			this.capacity = capacity;
			this.cost = cost;
		}
	}

	public static Edge[][] compileWD(int n, List<Edge> edges)
	{
		Edge[][] g = new Edge[n][];
		// cloning
		for(int i = edges.size()-1;i >= 0;i--){
			Edge origin = edges.get(i);
			Edge clone = new Edge(origin.to, origin.from, origin.capacity, -origin.cost);
			clone.flow = origin.capacity;
			clone.complement = origin;
			origin.complement = clone;
			edges.add(clone);
		}

		int[] p = new int[n];
		for(Edge e : edges)p[e.from]++;
		for(int i = 0;i < n;i++)g[i] = new Edge[p[i]];
		for(Edge e : edges)g[e.from][--p[e.from]] = e;
		return g;
	}

	// NOT VERIFIED
	public static Edge[][] compileWU(int n, List<Edge> edges)
	{
		Edge[][] g = new Edge[n][];
		// cloning
		for(int i = edges.size()-1;i >= 0;i--){
			Edge origin = edges.get(i);
			Edge back = new Edge(origin.to, origin.from, origin.capacity, origin.cost);
			edges.add(back);
		}
		for(int i = edges.size()-1;i >= 0;i--){
			Edge origin = edges.get(i);
			Edge clone = new Edge(origin.to, origin.from, origin.capacity, -origin.cost);
			clone.flow = origin.capacity;
			clone.complement = origin;
			origin.complement = clone;
			edges.add(clone);
		}

		int[] p = new int[n];
		for(Edge e : edges)p[e.from]++;
		for(int i = 0;i < n;i++)g[i] = new Edge[p[i]];
		for(Edge e : edges)g[e.from][--p[e.from]] = e;
		return g;
	}

	public static long solveMinCostFlow(Edge[][] g, int source, int sink, long all)
	{
		int n = g.length;
		long mincost = 0;
		int[] potential = new int[n];

		final int[] d = new int[n];
		MinHeap q = new MinHeap(n);
		while(all > 0){
			// shortest path src->sink
			Edge[] inedge = new Edge[n];
			Arrays.fill(d, Integer.MAX_VALUE / 2);
			d[source] = 0;
			q.add(source, 0);
			while(q.size() > 0){
				int cur = q.argmin();
				q.remove(cur);
				for(Edge ne : g[cur]){
					if(ne.capacity - ne.flow > 0){
						int nd = d[cur] + ne.cost + potential[cur] - potential[ne.to];
						if(d[ne.to] > nd){
							inedge[ne.to] = ne;
							d[ne.to] = nd;
							q.update(ne.to, nd);
						}
					}
				}
			}

			if(inedge[sink] == null)break;

			long minflow = all;
			long sumcost = 0;
			for(Edge e = inedge[sink];e != null;e = inedge[e.from]){
				if(e.capacity - e.flow < minflow)minflow = e.capacity - e.flow;
				sumcost += e.cost;
			}
			mincost += minflow * sumcost;
			for(Edge e = inedge[sink];e != null;e = inedge[e.from]){
				e.flow += minflow;
				e.complement.flow -= minflow;
			}

			all -= minflow;
			for(int i = 0;i < n;i++){
				potential[i] += d[i];
			}
		}
		return mincost;
	}

	public static class MinHeap {
		public int[] a;
		public int[] map;
		public int[] imap;
		public int n;
		public int pos;
		public static int INF = Integer.MAX_VALUE;

		public MinHeap(int m)
		{
			n = m+2;
			a = new int[n];
			map = new int[n];
			imap = new int[n];
			Arrays.fill(a, INF);
			Arrays.fill(map, -1);
			Arrays.fill(imap, -1);
			pos = 1;
		}

		public int add(int ind, int x)
		{
			int ret = imap[ind];
			if(imap[ind] < 0){
				a[pos] = x; map[pos] = ind; imap[ind] = pos;
				pos++;
				up(pos-1);
			}
			return ret != -1 ? a[ret] : x;
		}

		public int update(int ind, int x)
		{
			int ret = imap[ind];
			if(imap[ind] < 0){
				a[pos] = x; map[pos] = ind; imap[ind] = pos;
				pos++;
				up(pos-1);
			}else{
				int o = a[ret];
				a[ret] = x;
				up(ret);
				down(ret);
//				if(a[ret] > o){
//					up(ret);
//				}else{
//					down(ret);
//				}
			}
			return x;
		}

		public int remove(int ind)
		{
			if(pos == 1)return INF;
			if(imap[ind] == -1)return INF;

			pos--;
			int rem = imap[ind];
			int ret = a[rem];
			map[rem] = map[pos];
			imap[map[pos]] = rem;
			imap[ind] = -1;
			a[rem] = a[pos];
			a[pos] = INF;
			map[pos] = -1;

			up(rem);
			down(rem);
			return ret;
		}

		public int min() { return a[1]; }
		public int argmin() { return map[1]; }
		public int size() {	return pos-1; }

		private void up(int cur)
		{
			for(int c = cur, p = c>>>1;p >= 1 && a[p] > a[c];c>>>=1, p>>>=1){
				int d = a[p]; a[p] = a[c]; a[c] = d;
				int e = imap[map[p]]; imap[map[p]] = imap[map[c]]; imap[map[c]] = e;
				e = map[p]; map[p] = map[c]; map[c] = e;
			}
		}

		private void down(int cur)
		{
			for(int c = cur;2*c < pos;){
				int b = a[2*c] < a[2*c+1] ? 2*c : 2*c+1;
				if(a[b] < a[c]){
					int d = a[c]; a[c] = a[b]; a[b] = d;
					int e = imap[map[c]]; imap[map[c]] = imap[map[b]]; imap[map[b]] = e;
					e = map[c]; map[c] = map[b]; map[b] = e;
					c = b;
				}else{
					break;
				}
			}
		}
	}

	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 F().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.IOException;
import java.util.*;
import java.io.*;
public class ll {
    public static void main(String[] args) throws IOException {
        gg g = new gg();
    }

}
class gg {
    gg() throws IOException {
        FScanner fs = new FScanner(new InputStreamReader(System.in));
        PrintWriter pw = new PrintWriter(System.out);
        int n = fs.nextInt();
        char[] word = fs.nextLine().toCharArray();
        int[] vals = fs.iarr(n);
        int[] badones = new int[26];
        int m = 0;
        for (int i = 0; i<n/2; i++)
            if (word[i] == word[n-i-1])
            {
                badones[word[i]-'a']++;
                m++;
            }
        int max = 0;
        boolean bad = false;
        int val = -1;
        for (int i  = 0; i<26; i++)
            if (badones[i]*2>m)
            {
                bad = true;
                val = i;
            }
        if (!bad) {
            long sum = 0;
            for (int i = 0; i<n/2; i++)
                if (word[i] !=word[n-i-1])
                    sum+=vals[i]+vals[n-i-1];
                else sum+=Math.max(vals[i], vals[n-i-1]);
            pw.println(sum);
        } else {
            int others = badones[val]-(m-badones[val]);
            ArrayList<Integer> appr = new ArrayList<>();
            for (int i = 0; i<n/2; i++)
                if (word[i] != word[n-i-1] && word[i] != 'a'+val && word[n-i-1] != 'a'+val)
                    appr.add(Math.min(vals[i], vals[n-i-1]));
            appr.sort(null);
            long ans = 0;
            for (int i =0 ; i<n/2; i++) {
                if (word[i] == word[n-i-1])
                    ans+=Math.max(vals[i], vals[n-i-1]);
                else ans+=vals[i]+vals[n-i-1];
            }
            for (int i =0 ; i<others; i++)
                ans-=appr.get(i);
            pw.println(ans);
        }
        pw.close();
    }

    void add(long s, TreeMap<Long, Integer> map) {
        if (map.containsKey(s))
            map.put(s, map.get(s)+1);
        else map.put(s, 1);
    }
    long lowest(TreeMap<Long, Integer> map) {
        if (map.size() == 0)
            return -1;
        long v = map.firstKey();
        if (map.get(v) == 1) {
            map.remove(v);
        } else {
            map.put(v, map.get(v)-1);
        }
        return v;
    }

}

class FScanner {
    StringTokenizer st;
    BufferedReader reader;
    FScanner(InputStreamReader isr) throws IOException {
        reader = new BufferedReader(isr);
    }

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

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

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

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

    char nextChar() throws IOException {
        return (char) reader.read();
    }

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

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

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

    char[][] cmas2 (int n, int m) throws IOException {
        char[][] mas = new char[n][m];
        for (int i =0 ; i<n; i++)
            mas[i] = nextLine().toCharArray();
        return mas;
    }

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

}


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

public class MinCostMaxFlow {
    public static void main(String[] args) {
        InputReader in = new InputReader();
        OutputWriter out = new OutputWriter(System.out);
		int letters = 'z' - 'a' + 1;
        int n = in.nextInt();
        String s = in.next();
        char[] string = s.toCharArray();
        int[] letCount = new int[letters];
        for(int i = 0; i < n; ++i) {
            letCount[string[i] - 'a']++;
        }
        int[] b = new int[n];
        for(int i = 0; i < n; ++i) {
            b[i] = in.nextInt();
        }

        int source = 0;
        int charOffSet = 1;
        int pairOffSet = charOffSet + letters;
        int sinkOffSet = pairOffSet + n / 2;
        int sink = sinkOffSet;
        int vNum = sinkOffSet + 1;

        Graph g = new Graph(vNum, source, sink);
        //Source -> Letters -> Pairs
        for(int i = 0; i < letters; ++i) {
            g.addEdge(source, charOffSet + i, letCount[i], 0);
            for(int j = 0; j < n/2; ++j) {
                char c1 = string[j];
                char c2 = string[n - j - 1];
                if(c1 == 'a' + i) {
                    if(c1 == c2) {
                        g.addEdge(charOffSet + i, pairOffSet + j,
                            1, -Integer.max(b[j], b[n - j - 1]));
                    } else {
                        g.addEdge(charOffSet + i, pairOffSet + j, 1, -b[j]);
                    }
                } else {
                    if(c2 == 'a' + i) {
                        g.addEdge(charOffSet + i, pairOffSet + j, 1, -b[n - j - 1]);
                    } else {
                        g.addEdge(charOffSet + i, pairOffSet + j, 1, 0);
                    }
                }
            }
        }
        //Pairs -> Sink
        for(int i = 0; i < n/2; ++i) {
            g.addEdge(pairOffSet + i, sink, 2, 0);
        }

        int[] ans = g.getMinCostMaxFlow();
//		System.out.println("flow: " + ans[0]);
//		System.out.println("cost: " + -ans[1]);
        System.out.println(-ans[1]);
    }

    public 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 print(Object... objects) {
            for (int i = 0; i < objects.length; i++) {
                if (i != 0) {
                    writer.print(' ');
                }
                writer.print(objects[i]);
            }
        }

        public void println(Object... objects) {
            print(objects);
            writer.println();
        }

        public void printf(String format, Object... objects) {
            writer.printf(format, objects);
        }

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

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

    }

    static class InputReader
    {
        BufferedReader br;
        StringTokenizer st;

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

        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;
        }
    }
}

class Graph {
    private int vNum;
    private int src;
    private int sink;

    private int[][] res;
    private int[][] cost;
    private List<Edge> costGraph;
    private int[] p;
    private int[] d;

    private static class Edge {
      final int from;
      final int to;
      final int cost;

      Edge(int from, int to, int cost) {
          this.from = from;
          this.to = to;
          this.cost = cost;
      }
  }

    public int getVertexNumber() {
        return vNum;
    }

    public Graph(int vNum, int src, int sink) {
        this.vNum = vNum;
        this.src = src;
        this.sink = sink;
        res = new int[vNum][vNum];
        cost = new int[vNum][vNum];
        costGraph = new ArrayList<>();
        d = new int[vNum];
        p = new int[vNum];
    }

    public void addEdge(int from, int to, int capacity, int cost) {
        res[from][to] += capacity;
        this.cost[from][to] = cost;
        this.cost[to][from] = -cost;

    }

    private boolean findCheapestPath() {
    	costGraph.clear();
    	for(int i = 0; i < vNum; ++i) {
    		for(int j = 0; j < vNum; ++j) {
    			if(res[i][j] > 0) {
    				costGraph.add(new Edge(i, j, cost[i][j]));
    			}
    		}
    	}

    	Arrays.fill(d, Integer.MAX_VALUE);
    	Arrays.fill(p, -1);

    	d[src] = 0;

    	boolean changed = false;
    	for(int i = 0; i < vNum - 1; ++i) {
    		for(Edge e: costGraph) {
    			// Be careful here
    			// Overfloating can be
    			if(d[e.from] != Integer.MAX_VALUE && d[e.from] + e.cost < d[e.to]) {
    				d[e.to] = d[e.from] + e.cost;
    				p[e.to] = e.from;
    				changed = true;
    			}
    		}
    		if(!changed) {
    			break;
    		}
    	}

    	return p[sink] != -1;
    }

    private int[] augmentPath(int from, int to, int flow) {
    	int pathCost = 0;
    	for(int cur = to; cur != from; cur = p[cur]) {
    		flow = Integer.min(flow, res[p[cur]][cur]);
    	}
    	for(; to != from; to = p[to]) {
    		res[p[to]][to] -= flow;
    		res[to][p[to]] += flow;
    		pathCost += cost[p[to]][to] * flow;
    	}
    	return new int[] {flow, pathCost};
    }

    public int[] getMinCostMaxFlow() {
    	int flow = 0;
    	int totalCost = 0;
    	while(findCheapestPath()) {
    		int[] result = augmentPath(src, sink, Integer.MAX_VALUE);
    		flow += result[0];
    		totalCost += result[1];
    	}
    	return new int[] {flow, totalCost};
    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.util.InputMismatchException;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.Writer;
import java.io.OutputStreamWriter;
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);
        TaskF solver = new TaskF();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskF {
        static boolean debug = false;
        int numberNumber;
        int t;
        long[][] bin = IntegerUtils.generateBinomialCoefficients(100);

        static {
            if (debug) {
                try {
                    FileOutputStream f = new FileOutputStream("file.txt");
                    System.setErr(new PrintStream(f));
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }

        public void solve(int testNumber, InputReader in, OutputWriter out) {
            numberNumber = in.readInt();
            t = in.readInt();

//        t = 1;

            //        HashMap<Integer, String> answers = new HashMap<>();
//
//        for (int i = 1, cnt = 0; cnt < 1e5; i++) {
//            int[] cntTake = new int[16];
//            for (char c : String.format("%x", i).toCharArray()) {
//                ++cntTake[Integer.parseInt("" + c, 16)];
//            }
//            boolean ok = true;
//            for (int c : cntTake) {
//                ok &= c <= t;
//            }
//            if (ok) {
//                ++cnt;
//                String answer = String.format("%x", i);
//                answers.put(cnt, answer);
//                System.err.println(cnt + " = " + answer);
//                System.err.flush();
//            }
//        }

//
//        for (int _numberNumber = 1; ; _numberNumber++) {
//            numberNumber = _numberNumber;
            int wordLength = 1;
            for (; ; wordLength++) {
                long[][] dp = new long[16][wordLength + 1];

                for (int length = 0; length <= wordLength; length++) {
                    for (int ch = 0; ch < 16; ch++) {
                        if (length == 0) {
                            dp[ch][length] = 1;
                        } else if (ch == 0) {
                            if (length <= t) {
                                dp[ch][length] = bin[wordLength - 1][length];
                            }
                        } else {
                            dp[ch][length] += dp[ch - 1][length];
                            for (int c = 1; c <= Math.min(length, t); c++) {
                                dp[ch][length] += bin[wordLength - length + c][c] * dp[ch - 1][length - c];
                            }
                        }
                    }
                }

                System.err.println("wordLength, dp: " + wordLength + " " + dp[15][wordLength]);
                System.err.flush();
                if (dp[15][wordLength] < numberNumber) {
                    numberNumber -= dp[15][wordLength];
                } else {
//                out.printLine("wordLength: " + wordLength);
                    break;
                }
            }

            StringBuilder ans = new StringBuilder();
            int[] maxCnt = new int[16];
            Arrays.fill(maxCnt, t);
            for (int pos = 0; pos < wordLength; pos++) {
                boolean addCh = false;
                for (int ch = 0; ch < 16; ch++) {
                    if (pos == 0 && ch == 0) {
                        continue;
                    }
                    if (maxCnt[ch] == 0) {
                        continue;
                    }
                    --maxCnt[ch];
                    long dp = getDp(wordLength - pos - 1, maxCnt);
                    if (numberNumber <= dp) {
                        ans.append(String.format("%x", ch));
                        addCh = true;
                        break;
                    } else {
                        ++maxCnt[ch];
                        numberNumber -= dp;
                    }
                }
                if (!addCh) {
                    throw new RuntimeException("no addCh");
//                    out.printLine("no add ch at pos=" + pos, "   : ", _numberNumber, answers.get(_numberNumber), ans);
//                    return;
                }
            }
//            if (!answers.get(_numberNumber).equals(ans.toString())) {
//                out.printLine("FUCK:", _numberNumber, answers.get(_numberNumber), ans);
//                return;
//            }
            out.printLine(ans);
//        }
        }

        private long getDp(int wordLength, int[] maxCnt) {
            long[][] dp = new long[16][wordLength + 1];
            for (int length = 0; length <= wordLength; length++) {
                for (int ch = 0; ch < 16; ch++) {
                    if (length == 0) {
                        dp[ch][length] = 1;
                    } else {
                        if (ch != 0) {
                            dp[ch][length] += dp[ch - 1][length];
                        }
                        for (int cnt = 1; cnt <= Math.min(length, maxCnt[ch]); cnt++) {
                            if (ch == 0) {
                                if (length == cnt) {
                                    dp[ch][length] += bin[wordLength][cnt];
                                }
                            } else {
                                dp[ch][length] += bin[wordLength - length + cnt][cnt] * dp[ch - 1][length - cnt];
                            }
                        }
                    }
                }
            }
            return dp[15][wordLength];
        }

    }

    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 interface SpaceCharFilter {
            public boolean isSpaceChar(int ch);

        }

    }

    static class IntegerUtils {
        public static long[][] generateBinomialCoefficients(int n) {
            long[][] result = new long[n + 1][n + 1];
            for (int i = 0; i <= n; i++) {
                result[i][0] = 1;
                for (int j = 1; j <= i; j++) {
                    result[i][j] = result[i - 1][j - 1] + result[i - 1][j];
                }
            }
            return result;
        }

    }

    static class OutputWriter {
        private final PrintWriter writer;

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

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

        public void print(Object... objects) {
            for (int i = 0; i < objects.length; i++) {
                if (i != 0) {
                    writer.print(' ');
                }
                writer.print(objects[i]);
            }
        }

        public void printLine(Object... objects) {
            print(objects);
            writer.println();
        }

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

    }
}


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

public class Main implements Runnable{
    public static void main(String[] args){
        //new Thread(null,new Main(),"Thread-1",1024*1024*10).start();
        new Main().run();
    }
    int mod=1000000007;
    int n,k,m;
    private void solve() throws Exception {
        InputReader in = new InputReader(System.in);
        PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
        n=in.nextInt();k=in.nextInt();m=in.nextInt();
        int[][]ma=new int[(k+1)*(1<<m)][(k+1)*(1<<m)];
        for(int i=0;i<=k;i++) {
            for (int j = 0; j < (1 << m); j++) {
                ma[trans(i,j>>1)][trans(i,j)]=1;
                if(i<k)ma[trans(i+1,j>>1|(1<<m-1))][trans(i,j)]=Integer.bitCount(j)+1;
            }
        }
        ma=FastPowMod(ma,n);
        long ans=0;
        for(int i=trans(k,0);i<ma.length;i++)ans+=ma[i][0];
        out.println(ans%mod);
        out.flush();
    }
    public int[][] FastPowMod(int[][] a,int b){
        int[][] ans=new int[a.length][a.length];
        for(int i=0;i<a.length;i++)ans[i][i]=1;
        while(b>0){
            if((b&1)==1)
                ans=modMul(ans,a);
            a=modMul(a,a);
            b>>=1;
        }
        return ans;
    }
    public int[][] modMul(int[][]a,int[][]b){
        int row=a.length,column=b[0].length;
        int len=b.length;
        int[][] ans=new int[row][column];
        for(int i=0;i<row;i++){
            for(int j=0;j<len;j++){
                //if(a[i][j]==0)continue;//稀疏矩阵优化
                for(int k=0;k<column;k++){
                    ans[i][k]+=(long)a[i][j]*b[j][k]%mod;
                    ans[i][k]%=mod;
                }
            }
        }
        return ans;
    }

    int trans(int x,int y){
        return x*(1<<m)+y;
    }
    @Override
    public void run() {
        try {
            solve();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    void shuffle(int[]a,int l,int r){
        Random rnd=new Random();
        for (int i=r; i>l+1; i--) {
            int j=rnd.nextInt(i),tmp=a[j];
            a[j]=a[i-1];
            a[i-1]=tmp;
        }
    }
    void shuffle(Object[]a,int l,int r){
        Random rnd=new Random();
        for (int i=r; i>l+1; i--) {
            int j=rnd.nextInt(i);
            Object tmp=a[j];
            a[j]=a[i-1];
            a[i-1]=tmp;
        }
    }
    void shuffle(long[]a,int l,int r){
        Random rnd=new Random();
        for (int i=r; i>l+1; i--) {
            int j=rnd.nextInt(i);
            long tmp=a[j];
            a[j]=a[i-1];
            a[i-1]=tmp;
        }
    }
    class InputReader{
        StreamTokenizer tokenizer;
        public InputReader(InputStream stream){
            tokenizer=new StreamTokenizer(new BufferedReader(new InputStreamReader(stream)));
            tokenizer.ordinaryChars(33,126);
            tokenizer.wordChars(33,126);
        }
        public String next() throws IOException {
            tokenizer.nextToken();
            return tokenizer.sval;
        }
        public int nextInt() throws IOException {
            return Integer.parseInt(next());
        }
        public long nextLong() throws IOException {
            return Long.parseLong(next());
        }
        public boolean hasNext() throws IOException {
            int res=tokenizer.nextToken();
            tokenizer.pushBack();
            return res!=tokenizer.TT_EOF;
        }
    }
}


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

public class Main implements Runnable{
    public static void main(String[] args){
        //new Thread(null,new Main(),"Thread-1",1024*1024*10).start();
        new Main().run();
    }
    int mod=1000000007;
    int n,k,m;
    private void solve() throws Exception {
        InputReader in = new InputReader(System.in);
        PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
        n=in.nextInt();k=in.nextInt();m=in.nextInt();
        int[][]ma=new int[(k+1)*(1<<m)][(k+1)*(1<<m)];
        for(int i=0;i<=k;i++) {
            for (int j = 0; j < (1 << m); j++) {
                ma[trans(i,j>>1)][trans(i,j)]=1;
                if(i<k)ma[trans(i+1,j>>1|(1<<m-1))][trans(i,j)]=Integer.bitCount(j)+1;
            }
        }
        ma=FastPowMod(ma,n);
        long ans=0;
        for(int i=trans(k,0);i<ma.length;i++)ans+=ma[i][0];
        out.println(ans%mod);
        out.flush();
    }
    public int[][] FastPowMod(int[][] a,int b){
        int[][] ans=new int[a.length][a.length];
        for(int i=0;i<a.length;i++)ans[i][i]=1;
        while(b>0){
            if((b&1)==1)
                ans=modMul(ans,a);
            a=modMul(a,a);
            b>>=1;
        }
        return ans;
    }
    public int[][] modMul(int[][]a,int[][]b){
        int row=a.length,column=b[0].length;
        int len=b.length;
        int[][] ans=new int[row][column];
        for(int i=0;i<row;i++){
            for(int j=0;j<len;j++){
                if(a[i][j]==0)continue;//稀疏矩阵优化
                for(int k=0;k<column;k++){
                    ans[i][k]+=(long)a[i][j]*b[j][k]%mod;
                    ans[i][k]%=mod;
                }
            }
        }
        return ans;
    }

    int trans(int x,int y){
        return x*(1<<m)+y;
    }
    @Override
    public void run() {
        try {
            solve();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    void shuffle(int[]a,int l,int r){
        Random rnd=new Random();
        for (int i=r; i>l+1; i--) {
            int j=rnd.nextInt(i),tmp=a[j];
            a[j]=a[i-1];
            a[i-1]=tmp;
        }
    }
    void shuffle(Object[]a,int l,int r){
        Random rnd=new Random();
        for (int i=r; i>l+1; i--) {
            int j=rnd.nextInt(i);
            Object tmp=a[j];
            a[j]=a[i-1];
            a[i-1]=tmp;
        }
    }
    void shuffle(long[]a,int l,int r){
        Random rnd=new Random();
        for (int i=r; i>l+1; i--) {
            int j=rnd.nextInt(i);
            long tmp=a[j];
            a[j]=a[i-1];
            a[i-1]=tmp;
        }
    }
    class InputReader{
        StreamTokenizer tokenizer;
        public InputReader(InputStream stream){
            tokenizer=new StreamTokenizer(new BufferedReader(new InputStreamReader(stream)));
            tokenizer.ordinaryChars(33,126);
            tokenizer.wordChars(33,126);
        }
        public String next() throws IOException {
            tokenizer.nextToken();
            return tokenizer.sval;
        }
        public int nextInt() throws IOException {
            return Integer.parseInt(next());
        }
        public long nextLong() throws IOException {
            return Long.parseLong(next());
        }
        public boolean hasNext() throws IOException {
            int res=tokenizer.nextToken();
            tokenizer.pushBack();
            return res!=tokenizer.TT_EOF;
        }
    }
}


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

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

    int compress(int[] aa, int n) {
        Random rand = new Random();
        for (int i = 0; i < n; i++) {
            int j = rand.nextInt(i + 1);
            int tmp = aa[i]; aa[i] = aa[j]; aa[j] = tmp;
        }
        Arrays.sort(aa, 0, n);
        int m = 1;
        for (int i = 1; i < n; i++)
            if (aa[i] != aa[i - 1])
                aa[m++] = aa[i];
        return m;
    }
    void main() {
        int a = sc.nextInt();
        int b = sc.nextInt();
        int n = a + b;
        int s = (int) Math.sqrt(n) + 1;
        int[] gg = new int[s + s];
        int m = 0;
        for (int g = 1; g <= s; g++)
            gg[m++] = g;
        for (int k = 1; k <= s; k++) {
            int g = n / k;
	    gg[m++] = g;
        }
        m = compress(gg, m);
        int ans = 0;
        for (int h = 0; h < m; h++) {
            int g = gg[h];
            int al = (int) ((a + (long) g) / (g + 1));
            int bl = (int) ((b + (long) g) / (g + 1));
            int ar = a / g;
            int br = b / g;
            if (al <= ar && bl <= br) {
                int l = Math.max(n / (g + 1) + 1, al + bl);
                int r = Math.min(n / g, ar + br);
                if (l <= r)
                    ans += r - l + 1;
            }
        }
        println(ans);
    }
}


import java.util.*;

/**
 * Created by zhengyanglzy on 2017/6/24.
 */
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            long houseNum = sc.nextLong();
            long k = sc.nextLong();
            System.out.println(cal(houseNum, k));
        }
    }

    static long cal(long houseNum, long k) {
        if (k == 1) {
            return 1;

        } else if (k == 2) {
            return houseNum;
        }
        return 1 + findLabel(houseNum - 2, k - 2);
    }

    static long findLabel(long houseNum, long k) {
        // System.out.printf("%d %d\n", houseNum, k);
        long medium = (houseNum + 1) / 2;
        if (k == 1) {
            return medium;
        }
        k -= 1;
        Part leftPart = new Part(medium - 1);
        Part rightPart = new Part(houseNum - medium);
        long leftTotal = 0;
        long rightTotal = 0;
        while (true) {
            long left = leftPart.peek();
            long right = rightPart.peek();
            if (left == 0 && right == 0) {
                break;
            }
            if (k > left + right) {
                k -= left + right;
                leftTotal += left;
                rightTotal += right;
                leftPart.split();
                rightPart.split();
                continue;
            }
            leftPart.collapse();
            rightPart.collapse();
            TreeSet<Long> set = new TreeSet<Long>();
            set.addAll(leftPart.segs.keySet());
            set.addAll(rightPart.segs.keySet());
            for (long len : set.descendingSet()) {
                if (leftPart.segs.containsKey(len)) {
                    if (leftPart.segs.get(len) >= k) {
                        return findLabel(medium - 1, leftTotal + k);
                    } else {
                        k -= leftPart.segs.get(len);
                        leftTotal += leftPart.segs.get(len);
                    }
                }
                if (rightPart.segs.containsKey(len)) {
                    if (rightPart.segs.get(len) >= k) {
                        return medium + findLabel(houseNum - medium, rightTotal + k);
                    } else {
                        k -= rightPart.segs.get(len);
                        rightTotal += rightPart.segs.get(len);
                    }
                }
            }
        }
        // 只剩1和2
        if (medium - 1 - leftTotal >= k) {
            return findLabel(medium - 1, leftTotal + k);
        }
        return medium + findLabel(houseNum - medium, rightTotal + k - (medium - 1 - leftTotal));
    }

    static class Part {
        Map<Long, Long> segs;

        Part(long num) {
            segs = new HashMap<Long, Long>();
            segs.put(num, 1L);
        }

        void split() {
            Map<Long, Long> newMap = new HashMap<Long, Long>();
            for (Map.Entry<Long, Long> entry : segs.entrySet()) {
                if (entry.getKey() <= 2) {
                    update(newMap, entry.getKey(), entry.getValue());
                    continue;
                }
                long medium = (entry.getKey() + 1) / 2;
                update(newMap, medium - 1, entry.getValue());
                update(newMap, entry.getKey() - medium, entry.getValue());
            }
            segs = newMap;
        }

        long peek() {
            long ret = 0;
            for (Map.Entry<Long, Long> entry : segs.entrySet()) {
                if (entry.getKey() > 2) {
                    ret += entry.getValue();
                }
            }
            return ret;
        }

        void collapse() {
            Map<Long, Long> newMap = new HashMap<Long, Long>();
            for (Map.Entry<Long, Long> entry : segs.entrySet()) {
                update(newMap, (entry.getKey() + 1) / 2, entry.getValue());
            }
            segs = newMap;
        }

        static void update(Map<Long, Long> map, long key, long inc) {
            if (!map.containsKey(key)) {
                map.put(key, 0L);
            }
            map.put(key, map.get(key) + inc);
        }
    }

    static class Seg implements Comparable<Seg> {
        int start;
        int length;

        public Seg(int start, int length) {
            this.start = start;
            this.length = length;
        }

        public int compareTo(Seg o) {
            int diff = (length - 1) / 2 - (o.length -1) / 2;
            if (diff != 0) {
                return -diff;
            }
            return start - o.start;
        }
    }
}


import java.io.DataInputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.PriorityQueue;

public class C {
	static class Pair implements Comparable<Pair> {
		int node, depth;

		public Pair(int n, int v) {
			node = n;
			depth = v;
		}

		public int compareTo(Pair p) {
			return new Integer(p.depth).compareTo(depth);
		}
	}

	static int[] parent;

	public static void union(int x, int y) {
		if (find(x) != find(y)) {
			parent[find(y)] = find(x);
		}
	}

	public static int find(int x) {
		if (parent[x] == x)
			return x;
		return parent[x] = find(parent[x]);
	}

	static ArrayList<Integer>[] graph;
	static PriorityQueue<Pair> pq;
	static int[] haveToPush;
	static int maxSz;
	static int[] p;
	static int[] reqVol, curVol;
	static int rem;
	public static void dfs(int cur, int parent, int depth) {
		p[cur] = parent;
		if (curVol[cur] > reqVol[cur]) {
			haveToPush[cur] = Math.min(rem,curVol[cur] - reqVol[cur]);
			rem-=haveToPush[cur];
			if(haveToPush[cur]>0)
			pq.add(new Pair(cur, depth));
		}
		for (Integer next : graph[cur]) {
			if (next != parent) {
				dfs(next, cur, depth + 1);
			}
		}
	}

	public static void relax(int idx) {
		for (Pair p : pendingList[idx]) {
			int push=Math.min(p.depth,maxSz-curVol[idx]);
			curVol[p.node] -= push;
			curVol[idx] += push;
			trans++;
			res.add((p.node + 1) + " " + (idx + 1) + " " + (push));
		}
		for (Pair p : pendingList[idx]) {
			if (pendingList[p.node].size() > 0) {
				relax(p.node);
			}
		}
	}

	static ArrayList<String> res;
	static ArrayList<Pair>[] pendingList;
	static int trans;

	public static void main(String[] args) throws Exception {
		PrintWriter out = new PrintWriter(System.out);
		Parser aa=new Parser(System.in);
		int vesCnt = aa.nextLong();
		maxSz = aa.nextLong();
		int pipeCnt = aa.nextLong();
		graph = new ArrayList[vesCnt];
		curVol = new int[vesCnt];
		reqVol = new int[vesCnt];
		long curSum = 0, reqSum = 0;
		for (int i = 0; i < vesCnt; i++) {
			curVol[i] = aa.nextLong();
			curSum += curVol[i];
		}
		for (int i = 0; i < vesCnt; i++) {
			reqVol[i] = aa.nextLong();
			reqSum += reqVol[i];
		}
		if (curSum != reqSum) {
			System.out.println("NO");
			return;
		}
		parent = new int[vesCnt];
		for (int i = 0; i < vesCnt; i++) {
			parent[i] = i;
		}
		for (int i = 0; i < vesCnt; i++) {
			graph[i] = new ArrayList<Integer>();
		}
		for (int i = 0; i < pipeCnt; i++) {
			int source = aa.nextLong() - 1;
			int dest = aa.nextLong() - 1;
			if (find(source) != find(dest)) {
				graph[source].add(dest);
				graph[dest].add(source);
				union(source, dest);
			}
		}
		p = new int[vesCnt];
		res = new ArrayList<String>();
		for (int i = 0; i < vesCnt; i++) {
			if (curVol[i] < reqVol[i]) {
//				System.out.println(i+" "+res.size());
				Arrays.fill(p, -1);
				haveToPush = new int[vesCnt];
				int[] pending = new int[vesCnt];
				pendingList = new ArrayList[vesCnt];
				for (int j = 0; j < vesCnt; j++) {
					pendingList[j] = new ArrayList<Pair>();
				}
				pq = new PriorityQueue<Pair>();

//				System.out.println(Arrays.toString(curVol));
//				System.out.println(Arrays.toString(reqVol));
				rem = reqVol[i] - curVol[i];
				dfs(i, -1, 0);
//				System.out.println(Arrays.toString(haveToPush));
				boolean[] vis=new boolean[vesCnt];
				while ( !pq.isEmpty()) {
					Pair pp = pq.remove();
					if(vis[pp.node]) continue;
					vis[pp.node]=true;
					int toPush = haveToPush[pp.node]
							+ pending[pp.node];
					int from = pp.node;
					int to = p[pp.node];
					if (to == -1)
						break;
//					if(i==0){
//						System.out.println(curVol[to]+" "+toPush+"  a7"+" "+to+" "+from+" "+Arrays.toString(pending)+" "+Arrays.toString(haveToPush));
//					}
					if (maxSz - curVol[to] >= toPush) {
						trans++;
						res.add((from + 1) + " " + (to + 1) + " " + toPush);
						curVol[from] -= toPush;
						curVol[to] += toPush;
						haveToPush[to] += toPush;
						if (pendingList[from].size() > 0) {
							relax(from);
						}
						if(!vis[to])
						pq.add(new Pair(to, pp.depth - 1));
					} else {
						if (curVol[to] < maxSz) {
							trans++;
							res.add((from + 1) + " " + (to + 1) + " "
									+ (maxSz - curVol[to]));
							toPush -= (maxSz - curVol[to]);
							haveToPush[to] += (maxSz - curVol[to]);
							curVol[from] -= (maxSz - curVol[to]);
							curVol[to] = maxSz;
						}
						pending[to] += (toPush);
						pendingList[to].add(new Pair(from, toPush));
						if(!vis[to])
						pq.add(new Pair(to, pp.depth - 1));
					}
				}
				if (curVol[i] != reqVol[i]) {
					System.out.println("NO");
					return;
				}
			}
		}
		System.out.println(trans);
		for (int i = 0; i < res.size(); i++) {
			if (i % 1000 == 0)
				out.flush();
			out.println(res.get(i));
		}
		out.flush();
	}

	static class Parser {
		final private int BUFFER_SIZE = 1 << 16;

		private DataInputStream din;
		private byte[] buffer;
		private int bufferPointer, bytesRead;

		public Parser(InputStream in) {
			din = new DataInputStream(in);
			buffer = new byte[BUFFER_SIZE];
			bufferPointer = bytesRead = 0;
		}

		public char nextChar() throws Exception {
			byte c = read();
			while (c <= ' ')
				c = read();
			return (char) c;
		}

		public int nextLong() throws Exception {
			int ret = 0;
			byte c = read();
			while (c <= ' ')
				c = read();
			boolean neg = c == '-';
			if (neg)
				c = read();
			do {
				ret = ret * 10 + c - '0';
				c = read();
			} while (c > ' ');
			if (neg)
				return -ret;
			return ret;
		}

		private void fillBuffer() throws Exception {
			bytesRead = din.read(buffer, bufferPointer = 0, BUFFER_SIZE);
			if (bytesRead == -1)
				buffer[0] = -1;
		}

		private byte read() throws Exception {
			if (bufferPointer == bytesRead)
				fillBuffer();
			return buffer[bufferPointer++];
		}
	}
}


import java.util.List;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Comparator;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
import java.util.Collections;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 * @author vadimmm
 */
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 {

    private int vertexCounter, edgeCounter, value;
    private int[] path;
    private long[] initial, desired;
    private List<Integer>[] graph;
    private DSU dsu;
    private List<Integer> more, less;
    private boolean[] ready;
    private List<Transfusion> answer;

    public void solve(int testNumber, InputReader in, PrintWriter out) {

        vertexCounter = in.nextInt();
        value = in.nextInt();
        edgeCounter = in.nextInt();

        initial = new long[vertexCounter];
        desired = new long[vertexCounter];
        ready = new boolean[vertexCounter];
        more = new ArrayList<Integer>();
        less = new ArrayList<Integer>();
        answer = new ArrayList<Transfusion>();
        graph = new ArrayList[vertexCounter];
        for (int i = 0; i < vertexCounter; ++i)
            graph[i] = new ArrayList<Integer>();

        for (int i = 0; i < vertexCounter; ++i)
            initial[i] = in.nextInt();
        for (int i = 0; i < vertexCounter; ++i)
            desired[i] = in.nextInt();

        dsu = new DSU(vertexCounter, initial, desired);

        int v, u;
        for (int i = 0; i < edgeCounter; ++i) {
            v = in.nextInt() - 1;
            u = in.nextInt() - 1;
            graph[u].add(v);
            graph[v].add(u);
            dsu.union(v, u);
        }

        if (!dsu.check()) {
            out.println("NO");
            return;
        }

        for (int i = 0; i < vertexCounter; ++i)
            if (initial[i] == desired[i])
                ready[i] = true;
            else if (initial[i] > desired[i])
                more.add(i);
            else
                less.add(i);

        Comparator<Integer> cmp = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(dsu.getSet(o1), dsu.getSet(o2));
            }
        };
        Collections.sort(more, cmp);
        Collections.sort(less, cmp);

        int current = 0;
        for (int i = 0; i < more.size(); ++i)
            if (initial[more.get(i)] - desired[more.get(i)] > desired[less.get(current)] - initial[less.get(current)]) {
                transfusion(more.get(i), less.get(current), (int) (desired[less.get(current)] - initial[less.get(current)]));
                ready[less.get(current)] = true;
                current++;
                i--;
            } else if (initial[more.get(i)] - desired[more.get(i)] < desired[less.get(current)] - initial[less.get(current)]) {
                transfusion(more.get(i), less.get(current), (int) (initial[more.get(i)] - desired[more.get(i)]));
                ready[more.get(i)] = true;
            } else {
                transfusion(more.get(i), less.get(current), (int) (initial[more.get(i)] - desired[more.get(i)]));
                ready[more.get(i)] = true;
                ready[less.get(current)] = true;
                current++;
            }

        out.println(answer.size());
        for (Transfusion transfusion : answer)
            transfusion.println(out);
    }

    private void transfusion(int from, int to, int delta) {
        path = new int[vertexCounter];
        Arrays.fill(path, -1);
        bfs(to, from);
        flow(from, to, delta);
    }

    private void bfs(int start, int finish) {
        int[] queue = new int[vertexCounter];
        int size = 0;
        int next = 0;
        queue[size++] = start;
        while (next < size) {
            int v = queue[next++];
            for (int u : graph[v])
                if (path[u] == -1) {
                    path[u] = v;
                    if (u == finish)
                        return;
                    queue[size++] = u;
                }
        }
    }

    private void flow(int from, int to, int delta) {
        if (from == to)
            return;

        if (initial[path[from]] + delta <= value) {

                initial[from] -= delta;
                initial[path[from]] += delta;
            answer.add(new Transfusion(from, path[from], delta));

            flow(path[from], to, delta);
        } else if (initial[path[from]] >= delta) {
            flow(path[from], to, delta);

                initial[from] -= delta;
                initial[path[from]] += delta;
            answer.add(new Transfusion(from, path[from], delta));
        } else {
            int difference = (int) (delta - initial[path[from]]);

                initial[from] -= difference;
                initial[path[from]] += difference;
            answer.add(new Transfusion(from, path[from], difference));

            flow(path[from], to, delta);

                initial[from] -= delta - difference;
                initial[path[from]] += delta - difference;
            answer.add(new Transfusion(from, path[from], delta - difference));
        }
    }

}

class DSU {

    public int[] parent, rank;
    private long[] initial, desired;

    public DSU (int size, long[] initial, long[] desired) {
        parent = new int[size];
        rank = new int[size];
        this.initial = Arrays.copyOf(initial, initial.length);
        this.desired = Arrays.copyOf(desired, desired.length);
        for (int i = 0; i < size; ++i)
            parent[i] = i;
        for (int i = 0; i < size; ++i) {
            this.initial[getSet(i)] += initial[i];
            this.desired[getSet(i)] += desired[i];
        }
    }

    public int getSet(int v) {
        if (parent[v] == v)
            return v;
        return parent[v] = getSet(parent[v]);
    }

    public void union(int v, int u) {
        int a = getSet(v);
        int b = getSet(u);
        if (a != b) {
            if (rank[a] < rank[b]) {
                a ^= b;
                b ^= a;
                a ^= b;
            }
            parent[b] = a;
            if (rank[a] == rank[b])
                ++rank[a];
        }
    }

    public boolean check() {
        for (int v = 0; v < initial.length; ++v) {
            int p = getSet(v);
            if (p != v) {
                initial[p] += initial[v];
                desired[p] += desired[v];
                initial[v] = 0;
                desired[v] = 0;
            }
        }
        for (int i = 0; i < initial.length; ++i)
            if (initial[i] != desired[i])
                return false;
        return true;
    }

}

class Transfusion {

    public int from, to, value;

    public Transfusion(int x, int y, int v) {
        from = x;
        to = y;
        value = v;
    }

    public void println(PrintWriter out) {
        out.print(from + 1);
        out.print(" ");
        out.print(to + 1);
        out.print(" ");
        out.println(value);
    }

}

class InputReader {

    private static BufferedReader bufferedReader;
    private static StringTokenizer stringTokenizer;

    public InputReader(InputStream inputStream) {
        bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        stringTokenizer = null;
    }

    public String next() {
        while(stringTokenizer == null || !stringTokenizer.hasMoreTokens()) {
            try {
                stringTokenizer = new StringTokenizer(bufferedReader.readLine());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return stringTokenizer.nextToken();
    }

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

    }


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

public class Main{

    BufferedReader in;
    StringTokenizer str = null;
    PrintWriter out;

    private String next() throws Exception{
    	if (str == null || !str.hasMoreElements())
    	    str = new StringTokenizer(in.readLine());
    	return str.nextToken();
    }

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

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

    int n, e;
    long V;

    int [][]g;
    long []a, b, amount;
    boolean used[];

    long total = 0;
    int size = 0, yk = 0;
    int []comp, prev;

    int []from, to;
    public void run() throws Exception{
    	in = new BufferedReader(new InputStreamReader(System.in));
    	out = new PrintWriter(System.out);

	n = nextInt();
	V = nextLong();
	e = nextInt();

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

	from = new int[2*n*n];
	to = new int[2*n*n];
	amount = new long[2*n*n];

	g = new int[n][n];
	for(int i=0;i<e;i++){
	    int x = nextInt()-1, y = nextInt()-1;
	    g[x][y] = g[y][x] = 1;
	}

	comp = new int[n];
	prev = new int[n];
	Arrays.fill(prev, -1);
	used = new boolean[n];
	boolean ok  =true;

	for(int i=0;i<n;i++){
	    if (!used[i]){
		size = 0;
		total = 0;
		dfs(i);
		if (total != 0){
		    ok = false;
		    break;
		}
		boolean process = true;
		while(process){

		    process = false;

		    for(int j=0;j<size;j++){
			int v = comp[j];
			if (a[v] <= b[v]) continue;
			process = true;
			for(int k=0;k<size;k++){
			    prev[comp[k]] = -1;
			}
			parents(v);
			int u = -1;
			for(int k=0;k<size;k++){
			    int t = comp[k];
			    if (a[t] < b[t]){
				u = t;
				break;
			    }
			}
			long d = Math.min(a[v] - b[v], b[u] - a[u]);
			pour(v, u, d);
		    }
		}
	    }
	}

	if (!ok){
	    out.println("NO");
	}else{
	    out.println(yk);
	    for(int i=0;i<yk;i++){
		out.println(from[i] + " " + to[i] + " " + amount[i]);
	    }
	}
	out.close();
    }

    private void pour(int s, int t, long d){
	if (s == t) return;
	int parent = prev[t];
	long x = Math.min(d, a[parent]);
	x = Math.min(x, V - a[t]);
	add(parent, t, x);
	pour(s, parent, d);
	add(parent, t, d-x);
    }

    private void add(int s, int t, long d){
	if (d == 0) return;
	from[yk] = s+1;
	to[yk] = t+1;
	amount[yk++] = d;
	a[s]-=d;
	a[t]+=d;
    }

    private void parents(int x){
	for(int i=0;i<n;i++){
	    if (prev[i] < 0 && g[x][i] == 1) {
		prev[i] = x;
		parents(i);
	    }
	}
    }

    private void dfs(int x){
	used[x] = true;
	total+=a[x] - b[x];
	for(int i=0;i<n;i++){
	    if (!used[i] && g[x][i] == 1){
		dfs(i);
	    }
	}
	comp[size++] = x;
    }

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


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

public class Solution {

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

    int divs[] = new int[100];
    int divcnt = 0;

    public void solve() throws IOException {
        int n = nextInt();
        int x = nextInt();

        int npp = n + 1;
        for(int i = 2; i * i <= npp; ++i) {
            if(npp % i == 0) {
                out.println(-1);
                return;
            }
        }

        for(int i = 2; i * i <= n; ++i) {
            if(n % i == 0) {
                divs[divcnt++] = n / i;
            }
        }

        int stop = Math.max(2, x - n - 2);
        for(int b = x - 1; b >= stop; --b) {
            if(binpow(b, n, npp) == 1) {
                boolean ok = true;
                for(int d = 0; d < divcnt && ok; ++d) {
                    ok = binpow(b, divs[d], npp) != 1;
                }
                if(ok) {
                    out.println(b);
                    return;
                }
            }
        }

        out.println(-1);
    }

    int binpow(long a, int n, int mod) {
        long res = 1 % mod;
        while(n != 0) {
            if((n & 1) != 0) {
                res = res * a % mod;
            }
            a = a * a % mod;
            n >>= 1;
        }
        return (int)res;
    }

    BufferedReader br;
    StringTokenizer in;
    PrintWriter out;

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

        return in.nextToken();
    }

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

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

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

    void run() throws IOException {
        boolean oj = System.getProperty( "ONLINE_JUDGE" ) != null;
        Reader reader = new InputStreamReader( oj ? System.in : new FileInputStream("input.txt"));
        Writer writer = new OutputStreamWriter( oj ? System.out : new FileOutputStream("output.txt"));
        br = new BufferedReader(reader);
        out = new PrintWriter(writer);

        solve();

        out.close();
    }
}


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

public class Solution {
    private static int MOD = (1000000000+7);

    public static void main(String[] args) {
        FastReader sc = new FastReader();
        int n = sc.nextInt();
        int k = sc.nextInt();

        long ans = pow_mod(2,n);
        long pascalTriangle = 1;
        long m = pow_mod(2,n-k);
        for(int i = 0; i < n-k; i++){
            ans = sub(ans, mul(pascalTriangle,(m-1)));
            m = m * pow_mod(2, MOD-2) % MOD;
            long temp  = mul(pascalTriangle, (k+i));
            pascalTriangle = temp * pow_mod(i+1, MOD-2) % MOD;
        }

        System.out.println(ans);
    }

    public static long mul(long a, long b) {
        return (long) a * b % MOD;
    }

    private static long sub(long a, long b) {
        return a >= b ? a - b : a - b + MOD;
    }

    private static long pow_mod (long x, long n) {
        long y = 1;
        while (n > 0) {
            if (n%2 != 0)
                y = y * x % MOD;
            n = n/2;
            x = x * x % MOD;
        }
        return y;
    }

    private static int max(int a, int b){
        return Math.max(a, b);
    }

    private static int min(int a, int b){
        return Math.min(a, b);
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

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

        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 {
                if(st.hasMoreTokens()){
                    str = st.nextToken("\n");
                }
                else{
                    str = br.readLine();
                }
            }
            catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }

        int[] intArray(int n){
            int[] arr = new int[n];
            for(int i =0; i < n; i ++){
                arr[i] = nextInt();
            }
            return arr;
        }
    }
}


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

    static class HZSShufflesCards {
        NumberTheory.Mod998 mod = new NumberTheory.Mod998();
        int n;
        int m;

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

            mod.fInv(n + m);

            long phaseSize = 1;
            long p = 1;
            for (int i = 0; p != 0; i++) {
                p = mod.mult(p, n - i, mod.smallInv(n + m - i));
                phaseSize = mod.add(phaseSize, p);
            }

            long answer = 0;
            for (int k = 1; k <= n; k++) {
                long sign = mod.paritySign(k + 1);
                long numPhases = mod.mult(m + k, mod.smallInv(k));
                long term = mod.mult(sign, mod.ncr(n, k), numPhases, phaseSize);
                answer = mod.add(answer, term);
            }

            out.println(answer);
        }

    }

    static class InputReader {
        public final 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());
        }

    }

    static class Util {
        public static void ASSERT(boolean assertion) {
            if (!assertion)
                throw new AssertionError();
        }

        private Util() {
        }

    }

    static class IntStack {
        int[] array;
        int size = 0;

        public IntStack() {
            this(8);
        }

        public IntStack(int capacity) {
            array = new int[capacity];
        }

        public void push(int item) {
            if (size >= array.length)
                array = resize(array);
            array[size++] = item;
        }

        public int size() {
            return size;
        }

        public int get(int i) {
            Util.ASSERT(i < size);
            return array[i];
        }

        private static int[] resize(int[] array) {
            int[] newArray = new int[array.length << 1];
            System.arraycopy(array, 0, newArray, 0, array.length);
            return newArray;
        }

        public String toString() {
            int[] trimmed = new int[size];
            System.arraycopy(array, 0, trimmed, 0, size);
            return Arrays.toString(trimmed);
        }

    }

    static class NumberTheory {
        private static void ASSERT(boolean assertion) {
            if (!assertion)
                throw new AssertionError();
        }

        public abstract static class Modulus<M extends NumberTheory.Modulus<M>> {
            final IntStack factorial = new IntStack();
            final IntStack invFactorial = new IntStack();

            public abstract long modulus();

            public Modulus() {
                super();
                factorial.push(1);
                invFactorial.push(1);
            }

            public long fact(int n) {
                while (factorial.size() <= n) {
                    factorial.push((int) mult(factorial.get(factorial.size() - 1), factorial.size()));
                }

                return factorial.get(n);
            }

            public long fInv(int n) {
                int lastKnown = invFactorial.size() - 1;

                if (lastKnown < n) {
                    long[] fInv = new long[n - lastKnown];
                    fInv[0] = inv(fact(n));
                    for (int i = 1; i < fInv.length; i++) {
                        fInv[i] = mult(fInv[i - 1], n - i + 1);
                    }
                    for (int i = fInv.length - 1; i >= 0; i--) {
                        invFactorial.push((int) fInv[i]);
                    }
                }

                return invFactorial.get(n);
            }

            public long paritySign(long k) {
                return (k & 1) == 0 ? 1 : modulus() - 1;
            }

            public long ncr(int n, int r) {
                ASSERT(n >= 0);
                if (r < 0 || n < r)
                    return 0;
                return mult(fact(n), mult(fInv(r), fInv(n - r)));
            }

            public long smallInv(int n) {
                return mult(fInv(n), fact(n - 1));
            }

            public long normalize(long x) {
                x %= modulus();
                if (x < 0)
                    x += modulus();
                return x;
            }

            public long add(long a, long b) {
                long v = a + b;
                return v < modulus() ? v : v - modulus();
            }

            public long mult(long... x) {
                long r = 1;
                for (long i : x)
                    r = mult(r, i);
                return r;
            }

            public long mult(long a, long b) {
                return (a * b) % modulus();
            }

            public long inv(long value) {
                long g = modulus(), x = 0, y = 1;
                for (long r = value; r != 0; ) {
                    long q = g / r;
                    g %= r;

                    long temp = g;
                    g = r;
                    r = temp;

                    x -= q * y;

                    temp = x;
                    x = y;
                    y = temp;
                }

                ASSERT(g == 1);
                ASSERT(y == modulus() || y == -modulus());

                return normalize(x);
            }

        }

        public static class Mod998 extends NumberTheory.Modulus<NumberTheory.Mod998> {
            public long modulus() {
                return 998_244_353L;
            }

        }

    }
}


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

    static class HZSShufflesCards {
        NumberTheory.Mod998 mod = new NumberTheory.Mod998();
        int n;
        int m;

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

            mod.fInv(n + m);

            long phaseSize = 1;
            long p = 1;
            for (int i = 0; p != 0; i++) {
                p = mod.mult(p, n - i, mod.smallInv(n + m - i));
                phaseSize = mod.add(phaseSize, p);
            }

            long answer = 0;
            for (int k = 1; k <= n; k++) {
                long numPhases = mod.mult(m + k, mod.smallInv(k));
                long term = mod.mult(mod.ncr(n, k), numPhases, phaseSize);
                if ((k & 1) == 1)
                    answer = mod.add(answer, term);
                else
                    answer = mod.subtract(answer, term);
            }

            out.println(answer);
        }

    }

    static class InputReader {
        public final 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());
        }

    }

    static class Util {
        public static void ASSERT(boolean assertion) {
            if (!assertion)
                throw new AssertionError();
        }

        private Util() {
        }

    }

    static class IntStack {
        int[] array;
        int size = 0;

        public IntStack() {
            this(8);
        }

        public IntStack(int capacity) {
            array = new int[capacity];
        }

        public void push(int item) {
            if (size >= array.length)
                array = resize(array);
            array[size++] = item;
        }

        public int size() {
            return size;
        }

        public int get(int i) {
            Util.ASSERT(i < size);
            return array[i];
        }

        private static int[] resize(int[] array) {
            int[] newArray = new int[array.length << 1];
            System.arraycopy(array, 0, newArray, 0, array.length);
            return newArray;
        }

        public String toString() {
            int[] trimmed = new int[size];
            System.arraycopy(array, 0, trimmed, 0, size);
            return Arrays.toString(trimmed);
        }

    }

    static class NumberTheory {
        private static void ASSERT(boolean assertion) {
            if (!assertion)
                throw new AssertionError();
        }

        public abstract static class Modulus<M extends NumberTheory.Modulus<M>> {
            final IntStack factorial = new IntStack();
            final IntStack invFactorial = new IntStack();

            public abstract long modulus();

            public Modulus() {
                super();
                factorial.push(1);
                invFactorial.push(1);
            }

            public long fact(int n) {
                while (factorial.size() <= n) {
                    factorial.push((int) mult(factorial.get(factorial.size() - 1), factorial.size()));
                }

                return factorial.get(n);
            }

            public long fInv(int n) {
                int lastKnown = invFactorial.size() - 1;

                if (lastKnown < n) {
                    long[] fInv = new long[n - lastKnown];
                    fInv[0] = inv(fact(n));
                    for (int i = 1; i < fInv.length; i++) {
                        fInv[i] = mult(fInv[i - 1], n - i + 1);
                    }
                    for (int i = fInv.length - 1; i >= 0; i--) {
                        invFactorial.push((int) fInv[i]);
                    }
                }

                return invFactorial.get(n);
            }

            public long ncr(int n, int r) {
                ASSERT(n >= 0);
                if (r < 0 || n < r)
                    return 0;
                return mult(fact(n), mult(fInv(r), fInv(n - r)));
            }

            public long smallInv(int n) {
                return mult(fInv(n), fact(n - 1));
            }

            public long normalize(long x) {
                x %= modulus();
                if (x < 0)
                    x += modulus();
                return x;
            }

            public long add(long a, long b) {
                long v = a + b;
                return v < modulus() ? v : v - modulus();
            }

            public long subtract(long a, long b) {
                long v = a - b;
                return v < 0 ? v + modulus() : v;
            }

            public long mult(long... x) {
                long r = 1;
                for (long i : x)
                    r = mult(r, i);
                return r;
            }

            public long mult(long a, long b) {
                return (a * b) % modulus();
            }

            public long inv(long value) {
                long g = modulus(), x = 0, y = 1;
                for (long r = value; r != 0; ) {
                    long q = g / r;
                    g %= r;

                    long temp = g;
                    g = r;
                    r = temp;

                    x -= q * y;

                    temp = x;
                    x = y;
                    y = temp;
                }

                ASSERT(g == 1);
                ASSERT(y == modulus() || y == -modulus());

                return normalize(x);
            }

        }

        public static class Mod998 extends NumberTheory.Modulus<NumberTheory.Mod998> {
            public long modulus() {
                return 998_244_353L;
            }

        }

    }
}


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

    static class HZSShufflesCards {
        NumberTheory.Mod998 mod = new NumberTheory.Mod998();
        int n;
        int m;

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

            mod.fInv(n + m);

            long phaseSize = 1;
            long p = 1;
            for (int i = 0; p != 0; i++) {
                p = mod.mult(p, n - i, mod.smallInv(n + m - i));
                phaseSize = mod.add(phaseSize, p);
            }

            long answer = 0;
            for (int k = 1; k <= n; k++) {
                long numPhases = mod.mult(m + k, mod.smallInv(k));
                long e = mod.mult(numPhases, phaseSize);
                long term = mod.mult(mod.ncr(n, k), e);

                if (k % 2 == 1)
                    answer = mod.add(answer, term);
                else
                    answer = mod.subtract(answer, term);
            }

            out.println(answer);
        }

    }

    static class InputReader {
        public final 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());
        }

    }

    static class Util {
        public static void ASSERT(boolean assertion) {
            if (!assertion)
                throw new AssertionError();
        }

        private Util() {
        }

    }

    static class IntStack {
        int[] array;
        int size = 0;

        public IntStack() {
            this(8);
        }

        public IntStack(int capacity) {
            array = new int[capacity];
        }

        public void push(int item) {
            if (size >= array.length)
                array = resize(array);
            array[size++] = item;
        }

        public int size() {
            return size;
        }

        public int get(int i) {
            Util.ASSERT(i < size);
            return array[i];
        }

        private static int[] resize(int[] array) {
            int[] newArray = new int[array.length << 1];
            System.arraycopy(array, 0, newArray, 0, array.length);
            return newArray;
        }

        public String toString() {
            int[] trimmed = new int[size];
            System.arraycopy(array, 0, trimmed, 0, size);
            return Arrays.toString(trimmed);
        }

    }

    static class NumberTheory {
        private static void ASSERT(boolean assertion) {
            if (!assertion)
                throw new AssertionError();
        }

        public abstract static class Modulus<M extends NumberTheory.Modulus<M>> {
            final IntStack factorial = new IntStack();
            final IntStack invFactorial = new IntStack();

            public abstract long modulus();

            public Modulus() {
                super();
                factorial.push(1);
                invFactorial.push(1);
            }

            public long fact(int n) {
                while (factorial.size() <= n) {
                    factorial.push((int) mult(factorial.get(factorial.size() - 1), factorial.size()));
                }

                return factorial.get(n);
            }

            public long fInv(int n) {
                int lastKnown = invFactorial.size() - 1;

                if (lastKnown < n) {
                    long[] fInv = new long[n - lastKnown];
                    fInv[0] = inv(fact(n));
                    for (int i = 1; i < fInv.length; i++) {
                        fInv[i] = mult(fInv[i - 1], n - i + 1);
                    }
                    for (int i = fInv.length - 1; i >= 0; i--) {
                        invFactorial.push((int) fInv[i]);
                    }
                }

                return invFactorial.get(n);
            }

            public long ncr(int n, int r) {
                ASSERT(n >= 0);
                if (r < 0 || n < r)
                    return 0;
                return mult(fact(n), mult(fInv(r), fInv(n - r)));
            }

            public long smallInv(int n) {
                return mult(fInv(n), fact(n - 1));
            }

            public long normalize(long x) {
                x %= modulus();
                if (x < 0)
                    x += modulus();
                return x;
            }

            public long add(long a, long b) {
                long v = a + b;
                return v < modulus() ? v : v - modulus();
            }

            public long subtract(long a, long b) {
                long v = a - b;
                return v < 0 ? v + modulus() : v;
            }

            public long mult(long... x) {
                long r = 1;
                for (long i : x)
                    r = mult(r, i);
                return r;
            }

            public long mult(long a, long b) {
                return (a * b) % modulus();
            }

            public long inv(long value) {
                long g = modulus(), x = 0, y = 1;
                for (long r = value; r != 0; ) {
                    long q = g / r;
                    g %= r;

                    long temp = g;
                    g = r;
                    r = temp;

                    x -= q * y;

                    temp = x;
                    x = y;
                    y = temp;
                }

                ASSERT(g == 1);
                ASSERT(y == modulus() || y == -modulus());

                return normalize(x);
            }

        }

        public static class Mod998 extends NumberTheory.Modulus<NumberTheory.Mod998> {
            public long modulus() {
                return 998_244_353L;
            }

        }

    }
}


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

    static class HZSShufflesCards {
        NumberTheory.Mod998 mod = new NumberTheory.Mod998();
        int n;
        int m;

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

            mod.fInv(n + m);

            long phaseSize = 1;
            long p = 1;
            for (int i = 0; p != 0; i++) {
                p = mod.mult(p, n - i, mod.smallInv(n + m - i));
                phaseSize = mod.add(phaseSize, p);
            }

            long answer = 0;
            for (int k = 1; k <= n; k++) {
                long numPhases = mod.mult(m + k, mod.smallInv(k));
                long term = mod.mult(mod.ncr(n, k), numPhases, phaseSize);
                if (k % 2 == 1)
                    answer = mod.add(answer, term);
                else
                    answer = mod.subtract(answer, term);
            }

            out.println(answer);
        }

    }

    static class InputReader {
        public final 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());
        }

    }

    static class Util {
        public static void ASSERT(boolean assertion) {
            if (!assertion)
                throw new AssertionError();
        }

        private Util() {
        }

    }

    static class IntStack {
        int[] array;
        int size = 0;

        public IntStack() {
            this(8);
        }

        public IntStack(int capacity) {
            array = new int[capacity];
        }

        public void push(int item) {
            if (size >= array.length)
                array = resize(array);
            array[size++] = item;
        }

        public int size() {
            return size;
        }

        public int get(int i) {
            Util.ASSERT(i < size);
            return array[i];
        }

        private static int[] resize(int[] array) {
            int[] newArray = new int[array.length << 1];
            System.arraycopy(array, 0, newArray, 0, array.length);
            return newArray;
        }

        public String toString() {
            int[] trimmed = new int[size];
            System.arraycopy(array, 0, trimmed, 0, size);
            return Arrays.toString(trimmed);
        }

    }

    static class NumberTheory {
        private static void ASSERT(boolean assertion) {
            if (!assertion)
                throw new AssertionError();
        }

        public abstract static class Modulus<M extends NumberTheory.Modulus<M>> {
            final IntStack factorial = new IntStack();
            final IntStack invFactorial = new IntStack();

            public abstract long modulus();

            public Modulus() {
                super();
                factorial.push(1);
                invFactorial.push(1);
            }

            public long fact(int n) {
                while (factorial.size() <= n) {
                    factorial.push((int) mult(factorial.get(factorial.size() - 1), factorial.size()));
                }

                return factorial.get(n);
            }

            public long fInv(int n) {
                int lastKnown = invFactorial.size() - 1;

                if (lastKnown < n) {
                    long[] fInv = new long[n - lastKnown];
                    fInv[0] = inv(fact(n));
                    for (int i = 1; i < fInv.length; i++) {
                        fInv[i] = mult(fInv[i - 1], n - i + 1);
                    }
                    for (int i = fInv.length - 1; i >= 0; i--) {
                        invFactorial.push((int) fInv[i]);
                    }
                }

                return invFactorial.get(n);
            }

            public long ncr(int n, int r) {
                ASSERT(n >= 0);
                if (r < 0 || n < r)
                    return 0;
                return mult(fact(n), mult(fInv(r), fInv(n - r)));
            }

            public long smallInv(int n) {
                return mult(fInv(n), fact(n - 1));
            }

            public long normalize(long x) {
                x %= modulus();
                if (x < 0)
                    x += modulus();
                return x;
            }

            public long add(long a, long b) {
                long v = a + b;
                return v < modulus() ? v : v - modulus();
            }

            public long subtract(long a, long b) {
                long v = a - b;
                return v < 0 ? v + modulus() : v;
            }

            public long mult(long... x) {
                long r = 1;
                for (long i : x)
                    r = mult(r, i);
                return r;
            }

            public long mult(long a, long b) {
                return (a * b) % modulus();
            }

            public long inv(long value) {
                long g = modulus(), x = 0, y = 1;
                for (long r = value; r != 0; ) {
                    long q = g / r;
                    g %= r;

                    long temp = g;
                    g = r;
                    r = temp;

                    x -= q * y;

                    temp = x;
                    x = y;
                    y = temp;
                }

                ASSERT(g == 1);
                ASSERT(y == modulus() || y == -modulus());

                return normalize(x);
            }

        }

        public static class Mod998 extends NumberTheory.Modulus<NumberTheory.Mod998> {
            public long modulus() {
                return 998_244_353L;
            }

        }

    }
}


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

    static class HZSShufflesCards {
        NumberTheory.Mod998 mod = new NumberTheory.Mod998();
        int n;
        int m;

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

            long phaseSize = 1;
            long p = 1;
            for (int i = 0; p != 0; i++) {
                p = mod.mult(p, n - i, mod.smallInv(n + m - i));
                phaseSize = mod.add(phaseSize, p);
            }

            long answer = 0;
            for (int k = 1; k <= n; k++) {
                long numPhases = mod.mult(m + k, mod.smallInv(k));
                long term = mod.mult(mod.ncr(n, k), numPhases, phaseSize);
                if ((k & 1) == 1)
                    answer = mod.add(answer, term);
                else
                    answer = mod.subtract(answer, term);
            }

            out.println(answer);
        }

    }

    static class InputReader {
        public final 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());
        }

    }

    static class Util {
        public static void ASSERT(boolean assertion) {
            if (!assertion)
                throw new AssertionError();
        }

        private Util() {
        }

    }

    static class IntStack {
        int[] array;
        int size = 0;

        public IntStack() {
            this(8);
        }

        public IntStack(int capacity) {
            array = new int[capacity];
        }

        public void push(int item) {
            if (size >= array.length)
                array = resize(array);
            array[size++] = item;
        }

        public int size() {
            return size;
        }

        public int get(int i) {
            Util.ASSERT(i < size);
            return array[i];
        }

        private static int[] resize(int[] array) {
            int[] newArray = new int[array.length << 1];
            System.arraycopy(array, 0, newArray, 0, array.length);
            return newArray;
        }

        public String toString() {
            int[] trimmed = new int[size];
            System.arraycopy(array, 0, trimmed, 0, size);
            return Arrays.toString(trimmed);
        }

    }

    static class NumberTheory {
        private static void ASSERT(boolean assertion) {
            if (!assertion)
                throw new AssertionError();
        }

        public abstract static class Modulus<M extends NumberTheory.Modulus<M>> {
            final IntStack factorial = new IntStack();
            final IntStack invFactorial = new IntStack();

            public abstract long modulus();

            public Modulus() {
                super();
                factorial.push(1);
                invFactorial.push(1);
            }

            public long fact(int n) {
                while (factorial.size() <= n) {
                    factorial.push((int) mult(factorial.get(factorial.size() - 1), factorial.size()));
                }

                return factorial.get(n);
            }

            public long fInv(int n) {
                int lastKnown = invFactorial.size() - 1;

                if (lastKnown < n) {
                    long[] fInv = new long[n - lastKnown];
                    fInv[0] = inv(fact(n));
                    for (int i = 1; i < fInv.length; i++) {
                        fInv[i] = mult(fInv[i - 1], n - i + 1);
                    }
                    for (int i = fInv.length - 1; i >= 0; i--) {
                        invFactorial.push((int) fInv[i]);
                    }
                }

                return invFactorial.get(n);
            }

            public long ncr(int n, int r) {
                ASSERT(n >= 0);
                if (r < 0 || n < r)
                    return 0;
                return mult(fact(n), mult(fInv(r), fInv(n - r)));
            }

            public long smallInv(int n) {
                return mult(fInv(n), fact(n - 1));
            }

            public long normalize(long x) {
                x %= modulus();
                if (x < 0)
                    x += modulus();
                return x;
            }

            public long add(long a, long b) {
                long v = a + b;
                return v < modulus() ? v : v - modulus();
            }

            public long subtract(long a, long b) {
                long v = a - b;
                return v < 0 ? v + modulus() : v;
            }

            public long mult(long... x) {
                long r = 1;
                for (long i : x)
                    r = mult(r, i);
                return r;
            }

            public long mult(long a, long b) {
                return (a * b) % modulus();
            }

            public long inv(long value) {
                long g = modulus(), x = 0, y = 1;
                for (long r = value; r != 0; ) {
                    long q = g / r;
                    g %= r;

                    long temp = g;
                    g = r;
                    r = temp;

                    x -= q * y;

                    temp = x;
                    x = y;
                    y = temp;
                }

                ASSERT(g == 1);
                ASSERT(y == modulus() || y == -modulus());

                return normalize(x);
            }

        }

        public static class Mod998 extends NumberTheory.Modulus<NumberTheory.Mod998> {
            public long modulus() {
                return 998_244_353L;
            }

        }

    }
}