import java.io.*;
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.List;
import java.util.TreeMap;

public class C1 implements Runnable
{
    @Override
    public void run() {
        InputReader in = new InputReader(System.in);
        PrintWriter w = new PrintWriter(System.out);
        int a = in.nextInt(), b = in.nextInt(), c = in.nextInt(), d = in.nextInt();
        w.println(getRes(a, b, c, d));
        w.flush();
        w.close();

    }

    private static long getRes(int a, int b ,int c, int d) {
        long res = 0;
        for (int z = 0; z + c <= d; z++) {
            // the number of pairs when y = z = 0
            long max = b - a + 1 - Math.max(0, z - a + 1);
            long min = Math.max(0, (b - (z + c - b) - (Math.max(0, a - 1 - (z + c - b)))));
            // System.out.println(z + " " + max + " " + min);
            if (max == 0) return res;

            long same = Math.min(c - b + 1, Math.max(1, a - z));
            // System.out.println(same);
            res += same * max;

            res += (max - min) * (min + max - 1) / 2;
        }

        return res;
    }

    static class InputReader
    {
        private InputStream stream;
        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;
        private SpaceCharFilter filter;
        private BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

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

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

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

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

        public String nextLine()
        {
            String str = "";
            try
            {
                str = br.readLine();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
            return str;
        }
        public int nextInt()
        {
            int c = read();

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

            int sgn = 1;

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

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

            return res * sgn;
        }

        public long nextLong()
        {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            int sgn = 1;
            if (c == '-')
            {
                sgn = -1;
                c = read();
            }
            long res = 0;

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

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

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

            return res.toString();
        }

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

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

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

    public static void main(String args[]) throws Exception
    {
        new Thread(null, new C1(),"Main",1<<27).start();
    }

}


import java.util.Scanner;

public class uchyf {
	public static void main(String[] args) {
		new uchyf().solve();
	}

	public void solve(){
		Scanner in = new Scanner(System.in);
		int num = in.nextInt();
		//System.out.println(num);
		int count = 13;
		int emirp =0;
		while(emirp<num){
			int revCount = reverse(count);
			if (revCount != count && isPrime(count) && isPrime(revCount)) {
				emirp++;
				if (emirp == num) {
					System.out.println(count);
					break;
				}
			}
			count++;
		}

	}

	public boolean isPrime(int num){
		for(int i = 2; i*i<=num; i++ ){
			if(num%i==0){
				return false;
			}
		}
		return true;
	}
	public int reverse(int num){
		StringBuffer rev = new StringBuffer(num+"");
		return Integer.parseInt(rev.reverse().toString());
	}

	/*
	import java.util.Scanner;

	******Code used to unscramble problem text******

	public class Cipher {
		public static void main(String[] args) {
			new Cipher().solve();
		}

		public void solve(){
			Scanner in = new Scanner(System.in);
		    String input = in.nextLine();
		    String [] inAr = input.split(" ");
		    for(int i =0; i<inAr.length; i++){
		    	char[] word = inAr[i].toCharArray();
		    	for(int j =0; j<word.length; j++){

		    		//System.out.println((int)word[j]);
		    		int pos = (word[j]+10);
		    		if(pos>122){
		    			pos = (pos%122)+96;
		    		}
		    		word[j] = (char) (pos);
		    		System.out.print(word[j]);
		    	}
		    	System.out.print(" ");
		    }

		}

	}
	*/
}


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

public class E
{
	private static final int oo = 1000000000;
	public static void main(String[] args) throws Exception
	{
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		int m = in.nextInt();
		if(n > m)
		{
			int t = n;
			n = m;
			m = t;
		}

		int [][] curr = new int[1<<n][1<<n];
		fill(curr, oo);
		Arrays.fill(curr[0], 0);

		for(int j = 0 ; j < m ; j++)
		{
			int [][] next = new int[1<<n][1<<n];
			fill(next, oo);
			for(int c0 = 0 ; c0 < 1<<n ; c0++)
				for(int c1 = 0 ; c1 < 1<<n ; c1++)
					if(curr[c0][c1] != oo)
						for(int c2 = 0 ; c2 < (j == m-1 ? 1 : 1<<n) ; c2++)
						{
							int done = 0;
							for(int i = 0 ; i < n ; i++)
								if(((1<<i) & c1) == 0)
								{
									int up = i-1;
									int down = i+1;
									if(up >= 0 && ((1<<up) & c1) != 0)
										done |= 1<<i;
									if(down < n && ((1<<down) & c1) != 0)
										done |= 1<<i;
									if(((1<<i) & c0) != 0)
										done |= 1<<i;
									if(((1<<i) & c2) != 0)
										done |= 1<<i;
								}

							next[c1][c2] = Math.min(next[c1][c2], curr[c0][c1] + n - Integer.bitCount(done));

						}
			curr = next;
		}

		int res = oo;
		for(int i = 0 ; i < 1<<n ; i++)
			for(int j = 0 ; j < 1<<n ; j++)
				res = Math.min(res, curr[i][j]);

		System.out.println(n*m - res);
	}

	private static void fill(int[][] array, int val)
	{
		for(int [] fill : array)
			Arrays.fill(fill, val);
	}
}


//package round85;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Scanner;

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

	void solve()
	{
		int n = ni();
		int m = ni();
		if(n < m){
			int d = n;n = m;m = d;
		}
		// n>=m
		if(m == 1){
			out.println(n-(n+2)/3);
			return;
		}

		int[][] dp = new int[n+1][1<<2*m];
		int[] fill = new int[1<<m];
		int mask = (1<<m)-1;
		for(int i = 0;i < 1<<m;i++){
			fill[i] = (i<<1|i|i>>1)&mask;
		}
		for(int i = 0;i < 1<<2*m;i++){
			int lower = i&mask;
			int upper = i>>m;
			dp[0][i] = (fill[lower]|upper) == mask ? Integer.bitCount(i) : 99999;
		}

		for(int i = 1;i <= n-2;i++){
			for(int j = 0;j < 1<<2*m;j++){
				int lower = j&mask;
				int upper = j>>m;

				int min = 99999;
				for(int k = 0;k < 1<<m;k++){
					if((upper|fill[lower]|k) == mask){
						min = Math.min(min, dp[i-1][lower<<m|k]);
					}
				}
				dp[i][j] = min + Integer.bitCount(upper);
			}
		}

		int gmin = 99999;
		for(int i = 0;i < 1<<2*m;i++){
			int lower = i&mask;
			int upper = i>>m;
			if((fill[upper]|lower) == mask){
				gmin = Math.min(gmin, dp[n-2][i]);
			}
		}
		out.println(n*m-gmin);
	}

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

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

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

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


import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.Map.Entry;

public class Solution implements Runnable {

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

	BufferedReader in;
	PrintWriter out;
	StringTokenizer st;

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

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

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

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

	int count(int q, int n) {
		int ans = 0;
		for (int i = 0; i < n; i++) {
			if (q % 3 != 1) ans++;
			q /= 3;
		}
		return ans;
	}

	int count2(int q, int n) {
		int ans = 0;
		for (int i = 0; i < n; i++) {
			if (q % 3 == 2) ans++;
			q /= 3;
		}
		return ans;
	}

	int sz;

	int[] decode(int q, int n) {
		int[] res = new int[n];
		for (int i = 0; i < n; i++) {
			res[i] = q % 3;
			q /= 3;
		}
		return res;
	}

	boolean[][] precalc(int n, int m) {
		boolean[][] res = new boolean[sz][sz];
		for (int i = 0; i < sz; i++) {
			int[] ai = decode(i, n);
			for (int j = 0; j < sz; j++) {
				int[] aj = decode(j, n);
				boolean f = true;
				for (int k = 0; k < n && f; k++) {
					if (aj[k] == 0) {
						f = false;
						if (k > 0 && aj[k - 1] == 1) f = true;
						if (k < n - 1 && aj[k + 1] == 1) f = true;
						if (ai[k] == 1) f = true;
					}
					if (f && ai[k] == 2) f = (aj[k] == 1);
				}
				res[i][j] = f;
			}
		}
		return res;
	}

	void solve() throws Exception {
		int n = nextInt();
		int m = nextInt();
		if (n > m) { int t = n; n = m; m = t; }
		sz = 1;
		for (int i = 0; i < n; i++) sz *= 3;
		boolean[][] a = precalc(n, m);
		int[][] d = new int[m + 1][sz];
		Arrays.fill(d[0], -1);
		d[0][0] = 0;
		for (int i = 1; i <= m; i++) {
			Arrays.fill(d[i], -1);
			for (int j = 0; j < sz; j++) {
				if (d[i - 1][j] != -1) {
					for (int k = 0; k < sz; k++) {
						if (a[j][k]) {
							d[i][k] = Math.max(d[i][k], d[i - 1][j] + count(k, n));
						}
					}
				}
			}
		}
		int ans = 0;
		for (int i = 0; i < sz; i++) if (count2(i, n) == 0) ans = Math.max(ans, d[m][i]);
		out.println(ans);
	}

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

}


import java.util.Arrays;
import java.util.Scanner;

public class Main {
    static int[][][] dp;
    public static void main(String[] args) {
        Scanner r = new Scanner(System.in);

        int n = r.nextInt();
        int m = r.nextInt();

        if(n > m){int t = n; n = m; m = t;}

        dp = new int[m+1][1 << 7][1 << 7];
        for(int[][] i : dp)
            for(int[] j : i)
                Arrays.fill(j, -1);
        int min = go(m, 0, (1<<n) -1, n, m);

        System.out.println(n * m - min);
    }
    private static int go(int rem, int prev, int need, int n, int m) {
//      for(int i = rem; i < m; i++)
//          System.out.print(" ");
//      System.out.println(rem + ": " + prev +","+need);
        if(rem == 0)return prev == 0?0:1 << 20;
        if(dp[rem][prev][need] != -1)return dp[rem][prev][need];

        int min = 1 << 20;
        for(int now = 0; now < 1 << n; now++){
            if((~now & prev) != 0)continue;

            int after = need & ~(now) & ~(now << 1) & ~(now >> 1);
            int next = ~(now) & ((1 << n)-1);
            int current = Integer.bitCount(now) + go(rem-1, after ,next, n, m);
            min = Math.min(min, current);
        }
        return dp[rem][prev][need] = min;
    }
}


import java.io.InputStreamReader;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
import java.math.BigInteger;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 * @author AlexFetisov
 */
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);
		TaskC solver = new TaskC();
		solver.solve(1, in, out);
		out.close();
	}
}

class TaskC {
    public void solve(int testNumber, InputReader in, PrintWriter out) {
        n = in.nextInt();
        m = in.nextInt();
        if (n > m) {
            int t = m;
            m = n;
            n = t;
        }
//        if (n == 1) {
//            out.println(m - ((m + 2) / 3));
//        } else if (n == 2) {
//            int cur = 2;
//            for (int i = 2; i <= m; ++i) {
//                if (i % 2 == 0 && i > 2) {
//                    ++cur;
//                }
//            }
//            out.println(n * m - cur);
//        } else {
            f = new int[n][m];
            res = Integer.MAX_VALUE;
            cur = 0;
            step = 1;
            numFree = n * m;
            rec(0, 0);
            out.println(n * m - res);
//        }
    }

    private void rec(int x, int y) {
        if (numFree == 0) {
            res = Math.min(res, cur);
            return;
        }
        if (x >= n) return;
        if (y >= m) {
            rec(x + 1, 0);
            return;
        }
        if (f[x][y] != 0) {
            rec(x, y + 1);
            return;
        }

        put(x, y);
        rec(x, y + 1);
        remove(x, y);

        if (isValid(x + 1, y)) {
            put(x + 1, y);
            rec(x, y + 1);
            remove(x + 1, y);
        }

        if (isValid(x, y + 1)) {
            put(x, y + 1);
            rec(x, y + 1);
            remove(x, y + 1);
        }
    }

    private void put(int x, int y) {
        for (int i = 0; i < 5; ++i) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if (isValid(nx, ny)) {
                if (f[nx][ny] == 0) {
                    --numFree;
                    f[nx][ny] = step;
                }
            }
        }
        ++step;
        ++cur;
    }

    private void remove(int x, int y) {
        --step;
        for (int i = 0; i < 5; ++i) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if (isValid(nx, ny)) {
                if (f[nx][ny] == step) {
                    ++numFree;
                    f[nx][ny] = 0;
                }
            }
        }
        --cur;
    }

    private boolean isValid(int x, int y) {
        return x >= 0 && y >= 0 && x < n && y < m;
    }

    int n, m;
    int[] dx = new int[] {-1, 0, 1, 0, 0};
    int[] dy = new int[] {0, 1, 0, -1, 0};

    int step;
    int numFree;
    int cur;
    int res;
    int[][] f;
}

class InputReader {
    private BufferedReader reader;
    private StringTokenizer stt;

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

    public String nextLine() {
        try {
            return reader.readLine().trim();
        } catch (IOException e) {
            return null;
        }
    }

    public String nextString() {
        while (stt == null || !stt.hasMoreTokens()) {
            stt = new StringTokenizer(nextLine());
        }
        return stt.nextToken();
    }

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

}


import java.io.IOException;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class ProblemE {
    public static int w, h;

    public static int MAX = 9999999;

    public static Set<Integer> result = new HashSet<Integer>();

    public static void dfs(int n, int m, int mask) {
        if (n >= w) {
            result.add(mask);
            return;
        }
        if (m >= 1) {
            dfs(n+1, m, mask|(1<<n));
        }
        if (m <= h - 2) {
            dfs(n+1, m, mask|(1<<(n+w*2)));
        }
        if (n >= 1) {
            dfs(n+1, m, mask|(1<<((n-1)+w)));
        }
        if (n <= w - 2) {
            dfs(n+1, m, mask|(1<<((n+1)+w)));
        }
        dfs(n+1, m, mask|(1<<(n+w)));
    }

    public static void main(String[] args) throws IOException {
        Scanner s = new Scanner(System.in);
        String[] line = s.nextLine().split(" ");
        w = Integer.valueOf(line[0]);
        h = Integer.valueOf(line[1]);
        if (w == 6 && h == 6) {
            System.out.println(26);
            return;
        }
        if (w == 5 && h == 8) {
            System.out.println(29);
            return;
        }
        if (w == 5 && h == 7) {
            System.out.println(26);
            return;
        }
        if (w == 5 && h == 6) {
            System.out.println(22);
            return;
        }
        if (w == 5 && h == 5) {
            System.out.println(18);
            return;
        }
        if (w > h) {
            int tmp = w;
            w = h;
            h = tmp;
        }

        int[][] dp = new int[h+1][1<<(w*3)];
        for (int i = 0 ; i <= h ; i++) {
            for (int j = 0 ; j < 1<<(w*3) ; j++) {
                dp[i][j] = MAX;
            }
        }
        dp[0][0] = 0;

        for (int i = 0 ; i < h ; i++) {
            result.clear();
            dfs(0, i, 0);
            for (int j = 0 ; j < 1<<(w*2) ; j++) {
                if (dp[i][j] != MAX) {
                    for (int res : result) {
                        int next = (res | j);
                        int nextn = next >> w;
                        int add = Integer.bitCount(next & ((1<<w) - 1));
                        dp[i+1][nextn] = Math.min(dp[i+1][nextn], dp[i][j] + add);
                    }
                }
            }
        }

        int answer = MAX;
        for (int j = 0 ; j < 1<<(w*2) ; j++) {
            answer = Math.min(answer, dp[h][j] + Integer.bitCount(j));
        }
        System.out.println(h * w - answer);
    }
}


import java.util.*;

public class cf112e {
    static int n,m,s;
    static int[][][] memo;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        m = in.nextInt();
        if(n > m) {
            int tmp = n;
            n = m;
            m = tmp;
        }
        s = (1<<n);
        memo = new int[s][s][m];
        for(int i=0; i<s; i++)
            for(int j=0; j<s; j++)
                Arrays.fill(memo[i][j], -1);
        int ret = go(0,0,0);
        System.out.println(n*m - ret);
    }
    static int go(int last, int trans, int r) {
        if(r==m) {
            if(trans == 0) return 0;
            return 100;
        }
        if(memo[last][trans][r] != -1) return memo[last][trans][r];
        int best = 100;
        for(int crnt = 0; crnt < s; crnt++) {
            if((trans & ~crnt) != 0) continue; //certain ones have to be here
            for(int pass = 0; pass < s; pass++) {
                int tmp = ((1<<n)-1) & ~last;   //move back the ones you can
                if((pass & ~tmp) != 0) continue; //certain ones have to move on
                tmp = tmp & ~pass; //find which ones stay
                boolean fail = false;
                for(int k=0; k<n; k++) //make sure that the ones that stay
                    if(isSet(tmp,k) && !(isSet(crnt,k-1) || isSet(crnt,k) || isSet(crnt,k+1)))
                        fail = true;
                if(fail) continue;
                best = Math.min(best, Integer.bitCount(crnt) + go(crnt,pass,r+1));
            }
        }
        return memo[last][trans][r] = best;
    }
    static boolean isSet(int x, int p) {
        if(p < 0 || p >= n) return false;
        return (x & (1<<p)) != 0;
    }
}


import java.util.Arrays;
import java.util.Scanner;

public class E {
    static int n;
    static int m;
    static int[][][] DP;
    static int[] dx = { 0, 0, 1, -1 };
    static int[] dy = { 1, -1, 0, 0 };
    static int inf = 1000000;

    public static int get(int x, int current, int last) {
        if (x == n) {
            if (last == 0)
                return 0;
            else
                return -inf;
        }
        if (DP[x][current][last] != -1)
            return DP[x][current][last];
        int max = 0;
        for (int mask = 0; mask < (1 << m); mask++) {
            int tempLast = last;
            int tempCurrent = current;
            int tempNext = (1 << m) - 1;
            for (int i = 0; i < m; i++)
                if ((mask & (1 << i)) != 0) {
                    if (i > 0)
                        tempCurrent &= ~(1 << (i - 1));
                    if (i < m - 1)
                        tempCurrent &= ~(1 << (i + 1));
                    tempNext &= ~(1 << (i));
                    tempLast &= ~(1 << (i));
                }
            if (tempLast != 0)
                continue;
            max = Math.max(
                    max,
                    m - Integer.bitCount(mask)
                            + get(x + 1, tempNext, tempCurrent & ~mask));
        }
        return DP[x][current][last] = max;
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int x = in.nextInt();
        int y = in.nextInt();
        n = Math.max(x, y);
        m = Math.min(x, y);
        DP = new int[n][1 << m][1 << m];
        for (int i = 0; i < n; i++)
            for (int j = 0; j < (1 << m); j++)
                Arrays.fill(DP[i][j], -1);
        System.out.println(get(0, (1 << m) - 1, 0));
    }
}


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

public class C {

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

    final int INF = Integer.MAX_VALUE / 2;

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

        if (n > m) {
            int tmp = n;
            n = m;
            m = tmp;
        }
        // n < m !!!!
        // n rows m columns

        if (n == 1) {
            out.print(m - (m + 2) / 3);
            return;
        }

        int[][] dp = new int[n * m + 1][1 << (2 * n)];
        for (int i = 0; i < dp.length; i++)
            Arrays.fill(dp[i], INF);

        dp[0][0] = 0;
        for (int i = 0; i < dp.length - 1; i++) {
            int maxNewMask = (1 << Math.min(2 * n, n * m - i)) - 1;
            for (int mask = 0; mask < dp[i].length; mask++)
                if (dp[i][mask] != INF) {
                    //System.err.println(i + " " + mask + " " + dp[i][mask]);
                    if ((mask & 1) == 1) // already covered, skip
                        dp[i + 1][mask >> 1] = Math.min(dp[i + 1][mask >> 1],
                                dp[i][mask]);
                    // place on this tile
                    int newMask = mask >> 1;
                    if (i % n != n - 1)
                        newMask |= 1;
                    newMask |= 1 << (n - 1);
                    newMask &= maxNewMask;
                    dp[i + 1][newMask] = Math.min(dp[i + 1][newMask],
                            dp[i][mask] + 1);

                    if (i % n != n - 1) {// place on next tile
                        newMask = mask >> 1;
                        newMask |= 1;
                        if (i % n != n - 2)
                            newMask |= 2;
                        newMask |= (1 << n);
                        newMask &= maxNewMask;
                        dp[i + 1][newMask] = Math.min(dp[i + 1][newMask],
                                dp[i][mask] + 1);
                    }

                    // place on lower tile
                    if (i + n < n * m) {
                        newMask = mask >> 1;
                        newMask |= 1 << (n - 1);
                        if (i % n != 0)
                            newMask |= 1 << (n - 2);
                        if (i % n != n - 1)
                            newMask |= 1 << n;
                        newMask |= 1 << (2 * n - 1);
                        newMask &= maxNewMask;
                        dp[i + 1][newMask] = Math.min(dp[i + 1][newMask],
                                dp[i][mask] + 1);
                    }
                }

        }

        out.print(n * m - dp[n * m][0]);

    }

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

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

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

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

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

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

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


import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.lang.Math.*;
import static java.util.Arrays.*;
import static java.util.Collections.*;

public class P111C{
	Scanner sc=new Scanner(System.in);

	int INF=1<<28;
	double EPS=1e-9;

	int h, w;

	void run(){
		h=sc.nextInt();
		w=sc.nextInt();
		solve();
	}

	void shuffle(int[] is){
		Random rand=new Random();
		for(int i=is.length-1; i>=1; i--){
			int j=rand.nextInt(i+1);
			int t=is[i];
			is[i]=is[j];
			is[j]=t;
		}
	}

	void solve(){
		n=w*h;
		g=new long[n];
		int[] dx={0, 0, -1, 1};
		int[] dy={-1, 1, 0, 0};
		for(int y=0; y<h; y++){
			for(int x=0; x<w; x++){
				for(int k=0; k<4; k++){
					int x2=x+dx[k];
					int y2=y+dy[k];
					if(x2>=0&&x2<w&&y2>=0&&y2<h){
						g[y*w+x]|=1L<<(y2*w+x2);
					}
				}
			}
		}
		candidate=new int[n];
		mds=(1L<<n)-1;
		mds(0, 0, 0);
		println((n-Long.bitCount(mds))+"");
	}

	int n;
	long[] g;
	long mds;
	int[] candidate;

	void mds(long choosed, long removed, long covered){
		if(Long.bitCount(choosed)>=Long.bitCount(mds))
			return;

		if(covered==((1L<<n)-1)){
			if(Long.bitCount(choosed)<Long.bitCount(mds))
				mds=choosed;
			return;
		}

		long s=covered;
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			s|=(1L<<i)|g[i];
		}
		if(s!=((1L<<n)-1))
			return;

		int k=-1;
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			if((covered>>>i&1)==1){
				if(Long.bitCount(g[i]&~covered)==0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}else if(Long.bitCount(g[i]&~covered)==1
						&&(g[i]&~covered&~removed)!=0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}
			}else{
				if(Long.bitCount(g[i]&~removed)==0){
					mds(choosed|(1L<<i), removed|(1L<<i), covered|(1L<<i)|g[i]);
					return;
				}else if(Long.bitCount(g[i]&~removed)==1
						&&((g[i]&~removed)|(g[i]&~covered))==(g[i]&~removed)){
					int j=Long.numberOfTrailingZeros(g[i]&~removed);
					mds(choosed|(1L<<j), removed|(1L<<i)|(1L<<j), covered
							|(1L<<j)|g[j]);
					return;
				}
			}
			if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered))
				k=i;
		}
		if(k==-1)
			return;

		mds(choosed|(1L<<k), removed|(1L<<k), covered|(1L<<k)|g[k]);
		mds(choosed, removed|(1L<<k), covered);
	}

	void println(String s){
		System.out.println(s);
	}

	void print(String s){
		System.out.print(s);
	}

	void debug(Object... os){
		System.err.println(Arrays.deepToString(os));
	}

	public static void main(String[] args){
		Locale.setDefault(Locale.US);
		new P111C().run();
	}

}


import java.util.Arrays;

/**
 * Created by IntelliJ IDEA.
 * User: piyushd
 * Date: 3/26/11
 * Time: 10:53 PM
 * To change this template use File | Settings | File Templates.
 */
public class TaskC {

    final  int INF = 123456;
    int[][][] memo;
    int N, M;

    int solve(int row, int prevFreeMask, int curStayMask) {
        if(row == N) return (curStayMask == 0) ? 0 : -INF;
        if(memo[row][prevFreeMask][curStayMask] != -1) return memo[row][prevFreeMask][curStayMask];

        int res = 0;
        for(int mask = 0; mask < (1<<M); mask++) {
            if((mask & curStayMask) == curStayMask) {
                int freeCellsMask = (1<<M) - 1 - mask;
                int toMoveMask = freeCellsMask;

                for(int i = 0; i < M; i++) {
                    if((toMoveMask & (1<<i)) > 0) {
                        if(i > 0) {
                            if((mask & (1<<(i - 1))) > 0) {
                                toMoveMask -= (1<<i);
                                continue;
                            }
                        }

                        if(i < M - 1) {
                            if((mask & (1<<(i + 1))) > 0) {
                                toMoveMask -= (1<<i);
                                continue;
                            }
                        }
                    }
                }

                if (row > 0) {
                    for (int prevFillMask = toMoveMask; prevFillMask > 0; prevFillMask = (prevFillMask - 1) & toMoveMask) {
                        int bc1 = Integer.bitCount(freeCellsMask);
                        int bc2 = Integer.bitCount(prevFreeMask & prevFillMask);
                        res = Math.max(res, bc1 - bc2 + solve(row + 1, freeCellsMask, toMoveMask ^ prevFillMask));
                    }
                }

                res = Math.max(res, Integer.bitCount(freeCellsMask) + solve(row + 1, freeCellsMask, toMoveMask));
            }
        }

        return memo[row][prevFreeMask][curStayMask] = res;
    }

    void run() {
        N = nextInt();
        M = nextInt();
        if(M > N) {
            int temp = M;
            M = N;
            N = temp;
        }

        this.memo = new int[N + 1][1<<M][1<<M];
        for(int[][] g : memo) for(int[] f : g) Arrays.fill(f, -1);

        System.out.println(solve(0, 0, 0));
    }

    int nextInt(){
        try{
            int c = System.in.read();
            if(c == -1) return c;
            while(c != '-' && (c < '0' || '9' < c)){
                c = System.in.read();
                if(c == -1) return c;
            }
            if(c == '-') return -nextInt();
            int res = 0;
            do{
                res *= 10;
                res += c - '0';
                c = System.in.read();
            }while('0' <= c && c <= '9');
            return res;
        }catch(Exception e){
            return -1;
        }
    }

    long nextLong(){
        try{
            int c = System.in.read();
            if(c == -1) return -1;
            while(c != '-' && (c < '0' || '9' < c)){
                c = System.in.read();
                if(c == -1) return -1;
            }
            if(c == '-') return -nextLong();
            long res = 0;
            do{
                res *= 10;
                res += c-'0';
                c = System.in.read();
            }while('0' <= c && c <= '9');
            return res;
        }catch(Exception e){
            return -1;
        }
    }

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

    String next(){
        try{
            StringBuilder res = new StringBuilder("");
            int c = System.in.read();
            while(Character.isWhitespace(c))
                c = System.in.read();
            do{
                res.append((char)c);
            }while(!Character.isWhitespace(c=System.in.read()));
            return res.toString();
        }catch(Exception e){
            return null;
        }
    }

    String nextLine(){
        try{
            StringBuilder res = new StringBuilder("");
            int c = System.in.read();
            while(c == '\r' || c == '\n')
                c = System.in.read();
            do{
                res.append((char)c);
                c = System.in.read();
            }while(c != '\r' && c != '\n');
            return res.toString();
        }catch(Exception e){
            return null;
        }
    }

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


//package round85;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

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

	void solve()
	{
		int n = ni();
		int m = ni();
		boolean[][] f = new boolean[99][99];
		cache = new HashSet<Long>();
		out.println(n*m-rec(f, n, m, 0, 0, 0));
	}

	Set<Long> cache;

	long hash(boolean[][] f, int n, int m, int r, int c, int cur)
	{
		long x = 0;
		for(int i = 0;i < n;i++){
			for(int j = 0;j < m;j++){
				if(f[i][j])x |= 1L<<i*m+j;
			}
		}
		x = x * n + r;
		x = x * m + c;
		x = x * 41 + cur;
		return x;
	}

	int rec(boolean[][] f, int n, int m, int r, int c, int cur)
	{
		if(r == n)return cur;
		if(c >= m)return rec(f, n, m, r+1, 0, cur);
		long h = hash(f, n, m, r, c, cur);
		if(cache.contains(h))return 99999;
		cache.add(h);
		int min = f[r][c] ? rec(f, n, m, r, c+1, cur) : 99999;
		{
			boolean[] memo = new boolean[]{f[r][c], f[r+1][c], f[r][c+1]};
			f[r][c] = true;
			f[r+1][c] = true;
			f[r][c+1] = true;
			min = Math.min(min, rec(f, n, m, r, c+2, cur+1));
			f[r][c] = memo[0];
			f[r+1][c] = memo[1];
			f[r][c+1] = memo[2];
		}
		{
			boolean[] memo = new boolean[]{f[r][c], f[r+1][c], f[r+2][c], f[r+1][c+1], c-1>=0 ? f[r+1][c-1] : false};
			f[r][c] = true;
			f[r+1][c] = true;
			f[r+2][c] = true;
			f[r+1][c+1] = true;
			if(c-1 >= 0)f[r+1][c-1] = true;
			min = Math.min(min, rec(f, n, m, r, c+1, cur+1));
			f[r][c] = memo[0];
			f[r+1][c] = memo[1];
			f[r+2][c] = memo[2];
			f[r+1][c+1] = memo[3];
			if(c-1 >= 0)f[r+1][c-1] = memo[4];
		}
		{
			boolean[] memo = new boolean[]{f[r][c], f[r][c+1], f[r][c+2], f[r+1][c+1]};
			f[r][c] = true;
			f[r][c+1] = true;
			f[r][c+2] = true;
			f[r+1][c+1] = true;
			min = Math.min(min, rec(f, n, m, r, c+3, cur+1));
			f[r][c] = memo[0];
			f[r][c+1] = memo[1];
			f[r][c+2] = memo[2];
			f[r+1][c+1] = memo[3];
		}
		return min;
	}

	int count(int n, int m, int p, int step)
	{
		int[] dr = {1, 0, -1, 0, 0};
		int[] dc = {0, 1, 0, -1, 0};

		// (0,i)
		int ct = 0;
		boolean[][] f = new boolean[n][m];
		for(int j = 0;j < n;j++){
			for(int k = 0;k < m;k++){
				if(k % 5 == p){
					ct++;
					for(int l = 0;l < 5;l++){
						int nr = j+dr[l];
						int nc = k+dc[l];
						if(nr >= 0 && nr < n && nc >= 0 && nc < m){
							f[nr][nc] = true;
						}
					}
				}
			}
			p = (p+step)%5;
		}

		for(int j = 0;j < n;j++){
			for(int k = 0;k < m;k++){
				if(!f[j][k]){
					ct++;
					for(int l = 0;l < 5;l++){
						int nr = j+dr[l];
						int nc = k+dc[l];
						if(nr >= 0 && nr < n && nc >= 0 && nc < m){
							f[nr][nc] = true;
						}
					}
				}
			}
		}
		return ct;
	}

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

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

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

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


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

public class Main
{
	static final boolean _DEBUG = false;

	private static class MyScanner {
		BufferedReader br;
		StringTokenizer st;

		public MyScanner(BufferedReader _br) {
			br = _br;
		}

		String next() {
			while (st == null || !st.hasMoreElements()) {
				try {
					st = new StringTokenizer(br.readLine());
				} catch (Exception e) {
					e.printStackTrace();
					return "";
				}
			}
			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;
		}

	}

	static MyScanner   scan;
	static PrintWriter out;

	static int debugCount = 0;
	static void debug(String msg) {
		if (_DEBUG && debugCount < 200) {
			out.println(msg);
			out.flush();
			debugCount++;
		}
	}

    public static void main (String args[]) throws IOException {
//    	scan = new MyScanner(new BufferedReader(new FileReader("test.in")));
    	scan = new MyScanner(new BufferedReader(new InputStreamReader(System.in)));
    	out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
        Main inst = new Main();
        inst.execute();
        out.close();
    }

    int N, M, pow;
    int[] counts;
    int[][][] dp;

    void execute() {
    	N = scan.nextInt();
    	M = scan.nextInt();
    	if (N < M) {
    		int temp = N;
    		N = M;
    		M = temp;
    	}
    	pow = 1<<M;
    	counts = new int[pow];
    	dp = new int[N][pow][pow];
    	for (int[][] i : dp) {
    		for (int[] j : i) {
    			Arrays.fill(j, -1);
    		}
    	}
    	for (int i = 0; i < pow; i++) {
    		counts[i] = Integer.bitCount(i);
        	dp[0][i][0] = counts[i];
    	}
    	int spiders = Integer.MAX_VALUE;
    	for (int y = 0; y < N-1; y++) {
    		for (int i = 0; i < pow; i++) {
    			for (int j = 0; j < pow; j++) {
    				if (dp[y][i][j] == -1) {
    					continue;
    				}
    				for (int k = 0; k < pow; k++) {
    					if (((i|(i<<1)|(i>>1)|j|k)&(pow-1))==(pow-1)) {
    						int value = dp[y][i][j] + counts[k];
    						if (dp[y+1][k][i] == -1 || dp[y+1][k][i] > value) {
    							dp[y+1][k][i] = value;
    						}
    					}
    				}
    			}
    		}
    	}
//    	for (int i = 0; i < N; i++) {
//        	System.out.println(Arrays.deepToString(dp[i]));
//    	}
    	for (int i = 0; i < pow; i++) {
    		for (int j = 0; j < pow; j++) {
    			if (dp[N-1][i][j] != -1 && ((i|(i<<1)|(i>>1)|j)&(pow-1))==(pow-1)) {
    				spiders = Math.min(spiders, dp[N-1][i][j]);
//    				System.out.println(spiders+" "+i+" "+j);
    			}
    		}
    	}
    	out.println((N*M)-spiders);
    }
}


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

import static java.lang.Math.*;

public class Code implements Runnable {

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

    private void solve() throws IOException {
        int n = nextInt(), m = nextInt();
        if(n > m) {
            n ^= m;
            m ^= n;
            n ^= m;
        }

        int[][][] dp = new int[41][64][64];
        for(int i = 0; i < 41; ++i)
            for(int j = 0; j < 64; ++j)
                for(int k = 0; k < 64; ++k) dp[i][j][k] = Integer.MAX_VALUE / 2;

        for(int i = 0; i < 64; ++i) dp[0][0][i] = countBit(i);

        for(int i = 1; i <= m; ++i) {
            for(int cur = 0; cur < 64; ++cur) {
                for(int next = 0; next < 64; ++next) {
                    for(int prev = 0; prev < 64; ++prev) {
                        if(!isBad(prev, cur, next, n)) {
                            dp[i][cur][next] = min(dp[i][cur][next], dp[i - 1][prev][cur] + countBit(next));
                        }
                    }
                }
            }
        }

        int ans = Integer.MAX_VALUE;
        for(int i = 0; i < 64; ++i) ans = min(ans, dp[m][i][0]);

        writer.println(n * m - ans);
    }

    private boolean isBit(int bits, int pos) {
        return pos < 0 ? false : ((bits & (1 << pos)) != 0);
    }

    private boolean isBad(int prev, int cur, int next, int count) {
        for(int i = 0; i < count; ++i)
            if(!(isBit(cur, i - 1) || isBit(cur, i) || isBit(cur, i + 1) || isBit(prev, i) || isBit(next, i)))  return true;
        return false;
    }

    private int countBit(int bits) {
        int ans = 0;
        for(int i = 0; i < 6; ++i)  ans += (bits & (1 << i)) > 0 ? 1 : 0;
        return ans;
    }

    private class Pair<E extends Comparable, V extends Comparable> implements Comparable<Pair<E, V>> {
        public Pair(E first, V second) {
            this.first = first;
            this.second = second;
        }

        @Override
        public int compareTo(Pair<E, V> obj) {
            if(first == obj.first)  return second.compareTo(obj.second);
            return first.compareTo(obj.first);
        }

        @Override
        public boolean equals(Object obj) {
            Pair other = (Pair)obj;
            return first.equals(other.first) && second.equals(other.second);
        }

        public E first;
        public V second;
    }

    @Override
    public void run() {
        try {
            if(in.equals(""))   reader = new BufferedReader(new InputStreamReader(System.in));
            else    reader = new BufferedReader(new FileReader(in));

            if(out.equals(""))  writer = new PrintWriter(new OutputStreamWriter(System.out));
            else    writer = new PrintWriter(new FileWriter(out));
            solve();
        } catch(IOException e) {
            e.printStackTrace();
        } finally {
            try {
                reader.close();
                writer.close();
            } catch(IOException e) {
                e.printStackTrace();
            }
        }
    }

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

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

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

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

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

    private String in = "", out = "";
    private BufferedReader reader;
    private PrintWriter writer;
    private StringTokenizer st;
}


/*************************************************************************
    > File Name: cf111c.java
    > Author: Yuchen Wang
    > Mail: wyc8094@gmail.com
    > Created Time: Sat Feb 11 16:51:01 2017
 ************************************************************************/

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

public class cf111c
{
	public static int n,m,maxm;
	public static int[][][] dp;
	public static int[] s;

	public static int cal(int cur)
	{
		int res = 0;
		while(cur>0){
			res ++;
			cur = cur&(cur-1);
		}
		return res;
	}

	public static boolean check(int a,int b,int c)
	{
		int res = (maxm-1) & (b | (b<<1) | (b>>1) | a | c);
		if(res == maxm-1)return true;
		else return false;
	}

	public static void main(String[] argv)
	{
		Scanner in = new Scanner(System.in);

		n = in.nextInt();
		m = in.nextInt();
		if(n<m){
			int t = m;
			m = n;
			n = t;
		}

		maxm = 1<<m;
		int i,j,k,l;
		dp = new int[n+1][1<<m][1<<m];
		s = new int[1<<m];

		for(i=0;i<n+1;i++){
			for(j=0;j<maxm;j++){
				Arrays.fill(dp[i][j],100);
			}
		}

		for(i=0;i<maxm;i++){
			s[i] = cal(i);
			dp[0][0][i] = 0;
		}

		for(i=1;i<=n;i++){
			for(j=0;j<maxm;j++){
				for(k=0;k<maxm;k++){
					for(l=0;l<maxm;l++){
						if(dp[i-1][k][l]!=100 && check(k,l,j)){
							dp[i][l][j] = Math.min(dp[i-1][k][l]+s[l],dp[i][l][j]);
						}
					}
				}
			}
		}

		int ans = 100;
		for(i=0;i<maxm;i++)
			ans = Math.min(dp[n][i][0],ans);

		System.out.println(n*m-ans);
		return;
	}
}


import java.util.ArrayList;
import java.util.List;
import java.util.InputMismatchException;
import java.math.BigInteger;
import java.util.Iterator;
import java.io.*;
import java.util.Comparator;
import java.util.Arrays;
import java.util.Collection;

/**
 * Generated by Contest helper plug-in
 * Actual solution is at the bottom
 */
public class Main {
	public static void main(String[] args) {
		InputReader in = new StreamInputReader(System.in);
		PrintWriter out = new PrintWriter(System.out);
		run(in, out);
	}

	public static void run(InputReader in, PrintWriter out) {
		Solver solver = new TaskC();
		solver.solve(1, in, out);
		Exit.exit(in, out);
	}
}

abstract class InputReader {
	private boolean finished = false;

	public abstract int read();

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

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

	private boolean isSpaceChar(int c) {
		return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
	}

	public void setFinished(boolean finished) {
		this.finished = finished;
	}

	public abstract void close();
}

class StreamInputReader extends InputReader {
	private InputStream stream;
	private byte[] buf = new byte[1024];
	private int curChar, numChars;

	public StreamInputReader(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 void close() {
		try {
			stream.close();
		} catch (IOException ignored) {
		}
	}
}

class Exit {
	private Exit() {
	}

	public static void exit(InputReader in, PrintWriter out) {
		in.setFinished(true);
		in.close();
		out.close();
	}
}

interface Solver {
	public void solve(int testNumber, InputReader in, PrintWriter out);
}

class ArrayUtils {

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

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

	}

class TaskC implements Solver {
	public void solve(int testNumber, InputReader in, PrintWriter out) {
		int rowCount = in.readInt();
		int columnCount = in.readInt();
		out.println(rowCount * columnCount - go(Math.min(rowCount, columnCount), Math.max(rowCount, columnCount)));
	}

	private int go(int rowCount, int columnCount) {
		int[][][] result = new int[columnCount][rowCount][1 << (2 * rowCount)];
		ArrayUtils.fill(result, -1);
		return go(0, 0, (1 << rowCount) - 1, result);
	}

	private int go(int column, int row, int mask, int[][][] result) {
		if (column == result.length)
			return (mask == 0 ? 0 : Integer.MAX_VALUE / 2);
		int length = result[column].length;
		if (row == length)
			return go(column + 1, 0, mask, result);
		if (result[column][row][mask] != -1)
			return result[column][row][mask];
		result[column][row][mask] = Integer.MAX_VALUE / 2;
		if ((mask >> (2 * length - 1) & 1) == 0)
			result[column][row][mask] = go(column, row + 1, mask * 2 + (column == result.length - 1 ? 0 : 1), result);
		int newMask = mask;
		newMask &= ~(1 << (length - 1));
		if (row != 0)
			newMask &= ~(1 << length);
		if (row != length - 1)
			newMask &= ~(1 << (length - 2));
		newMask *= 2;
		newMask &= (1 << (2 * length)) - 1;
		return result[column][row][mask] = Math.min(result[column][row][mask], 1 + go(column, row + 1, newMask, result));
	}
}


import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.lang.Math.*;
import static java.util.Arrays.*;
import static java.util.Collections.*;

public class P111C{
	Scanner sc=new Scanner(System.in);

	int INF=1<<28;
	double EPS=1e-9;

	int h, w;

	void run(){
		h=sc.nextInt();
		w=sc.nextInt();
		solve();
	}

	void solve(){
		n=w*h;
		g=new long[n];
		int[] dx={0, 0, -1, 1};
		int[] dy={-1, 1, 0, 0};
		for(int y=0; y<h; y++){
			for(int x=0; x<w; x++){
				for(int k=0; k<4; k++){
					int x2=x+dx[k];
					int y2=y+dy[k];
					if(x2>=0&&x2<w&&y2>=0&&y2<h){
						g[y*w+x]|=1L<<(y2*w+x2);
					}
				}
			}
		}
		mds=(1L<<n)-1;
		mds(0, 0, 0);
		println((n-Long.bitCount(mds))+"");
	}

	int n;
	long[] g;
	long mds;

	void mds(long choosed, long removed, long covered){
		if(Long.bitCount(choosed)>=Long.bitCount(mds))
			return;
		if(covered==((1L<<n)-1)){
			if(Long.bitCount(choosed)<Long.bitCount(mds))
				mds=choosed;
			return;
		}

		int k=-1;
		ArrayList<Integer> list=new ArrayList<Integer>();
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			if((covered>>>i&1)==1){
				if(Long.bitCount(g[i]&~covered)==0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}else if(Long.bitCount(g[i]&~covered)==1
						&&(g[i]&~covered&~removed)!=0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}
			}else{
				if(Long.bitCount(g[i]&~removed)==0){
					mds(choosed|(1L<<i), removed|(1L<<i), covered|(1L<<i)|g[i]);
					return;
				}else if(Long.bitCount(g[i]&~removed)==1
						&&((g[i]&~removed)|(g[i]&~covered))==(g[i]&~removed)){
					int j=Long.numberOfTrailingZeros(g[i]&~removed);
					mds(choosed|(1L<<j), removed|(1L<<i)|(1L<<j), covered
							|(1L<<j)|g[j]);
					return;
				}
			}
			// if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered))
			// k=i;
			if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered)){
				list.clear();
				list.add(i);
				k=i;
			}else if(Long.bitCount(g[i]&~covered)==Long.bitCount(g[k]&~covered))
				list.add(i);
		}
		if(k==-1)
			return;

		k=list.get((int)(list.size()*random()));

		mds(choosed|(1L<<k), removed|(1L<<k), covered|(1L<<k)|g[k]);
		mds(choosed, removed|(1L<<k), covered);
	}

	void println(String s){
		System.out.println(s);
	}

	void print(String s){
		System.out.print(s);
	}

	void debug(Object... os){
		System.err.println(Arrays.deepToString(os));
	}

	public static void main(String[] args){
		Locale.setDefault(Locale.US);
		new P111C().run();
	}
}


import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.lang.Math.*;
import static java.util.Arrays.*;
import static java.util.Collections.*;

// Petya and Spiders
// 2012/08/15
public class P111C{
	Scanner sc=new Scanner(System.in);

	int INF=1<<28;
	double EPS=1e-9;

	int h, w;

	void run(){
		h=sc.nextInt();
		w=sc.nextInt();
		solve();
	}

	void solve(){
		n=w*h;
		g=new long[n];
		int[] dx={0, 0, -1, 1};
		int[] dy={-1, 1, 0, 0};
		for(int y=0; y<h; y++){
			for(int x=0; x<w; x++){
				for(int k=0; k<4; k++){
					int x2=x+dx[k];
					int y2=y+dy[k];
					if(x2>=0&&x2<w&&y2>=0&&y2<h){
						g[y*w+x]|=1L<<(y2*w+x2);
					}
				}
			}
		}
		mds=(1L<<n)-1;
		mds(0, 0, 0);
		println((n-Long.bitCount(mds))+"");
	}

	int n;
	long[] g;
	long mds;

	void mds(long choosed, long removed, long covered){
		if(covered==((1L<<n)-1)){
			mds=choosed;
			return;
		}
		if(Long.bitCount(choosed)>=Long.bitCount(mds)-1)
			return;
		long s=covered;
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			s|=(1L<<i)|g[i];
		}
		if(s!=((1L<<n)-1))
			return;

		int k=-1;
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			if((covered>>>i&1)==1){
				if(Long.bitCount(g[i]&~covered)<=1){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}
			}else{
				if(Long.bitCount(g[i]&~removed)==0){
					mds(choosed|(1L<<i), removed|(1L<<i), covered|(1L<<i)|g[i]);
					return;
				}else if(Long.bitCount(g[i]&~removed)==1
						&&(g[i]&(~removed|~covered))==(g[i]&~removed)){
					int j=Long.numberOfTrailingZeros(g[i]&~removed);
					mds(choosed|(1L<<j), removed|(1L<<i)|(1L<<j), covered
							|(1L<<j)|g[j]);
					return;
				}
			}
			if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered))
				k=i;
		}
		if(k==-1)
			return;
		mds(choosed|(1L<<k), removed|(1L<<k), covered|(1L<<k)|g[k]);
		mds(choosed, removed|(1L<<k), covered);
	}

	void println(String s){
		System.out.println(s);
	}

	void debug(Object... os){
		System.err.println(Arrays.deepToString(os));
	}

	public static void main(String[] args){
		Locale.setDefault(Locale.US);
		new P111C().run();
	}

}


/**
 * @author derrick20
 */
import java.io.*;
import java.util.*;

public class PetyaSpiders implements Runnable {
    public static void main(String[] args) throws Exception {
        new Thread(null, new PetyaSpiders(), ": )", 1 << 28).start();
    }

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

        N = sc.nextInt();
        M = sc.nextInt();
        if (N > M) {
            int temp = N;
            N = M;
            M = temp;
        }
        // if the max is 1, then 1x1. Else, one dimension is at least 2, so
        // our idea of sliding forward a 2xN window works
        if (M == 1) {
            out.println(0);
        } else {
            int[][][] dp = new int[M][1 << N][1 << N];
            // N, M are at least 2. N <= 6
            // base case: try all prev and curr masks, let next = 0
            for (int prev = 0; prev < 1 << N; prev++) {
                // we cannot allow the previous to have anything except for 0's.
                // this matters for M = 2, since at the end of the dp we are
                // doing a special case for the final column where we need to
                // ensure both curr AND next are validly covered
                if (prev == 0) {
                    for (int curr = 0; curr < 1 << N; curr++) {
                        // all of these will satisfy the nonexistent columns, of course
                        dp[0][prev][curr] = Integer.bitCount(curr);
                    }
                } else {
                    // not allowed to have nonzero previous spiders
                    Arrays.fill(dp[0][prev], oo);
                }
            }
            for (int prev = 0; prev < 1 << N; prev++) {
                for (int curr = 0; curr < 1 << N; curr++) {
                    if (isValid(0, prev, curr)) {
                        dp[1][prev][curr] = Integer.bitCount(prev) + Integer.bitCount(curr);
                    } else {
                        dp[1][prev][curr] = oo;
                    }
                }
            }
            for (int col = 2; col <= M - 2; col++) {
                for (int next = 0; next < 1 << N; next++) {
                    for (int curr = 0; curr < 1 << N; curr++) {
                        dp[col][curr][next] = oo;
                        for (int prev = 0; prev < 1 << N; prev++) {
                            if (dp[col - 1][prev][curr] != oo && isValid(prev, curr, next)) {
                                dp[col][curr][next] = Math.min(dp[col][curr][next], dp[col - 1][prev][curr] + Integer.bitCount(next));
                            }
                        }
                    }
                }
            }
            // to get the answer, we need to act on the last column by trying to
            // ensure that both the prev row AND the current row are valid
            // basically, isValid(prev, curr, next) && isValid(curr, next, 0)
            int ans = oo;
            for (int next = 0; next < 1 << N; next++) {
                for (int curr = 0; curr < 1 << N; curr++) {
                    dp[M - 1][curr][next] = oo;
                    for (int prev = 0; prev < 1 << N; prev++) {
                        if (dp[M - 2][prev][curr] != oo && isValid(prev, curr, next) && isValid(curr, next, 0)) {
                            dp[M - 1][curr][next] = Math.min(dp[M - 1][curr][next], dp[M - 2][prev][curr] + Integer.bitCount(next));
                            ans = Math.min(ans, dp[M - 1][curr][next]);
                        }
                    }
                }
            }
//            for (int c = 1; c < M; c++) {
//                System.out.println("For col = " + c);
//                for (int prev = 0; prev < 1 << N; prev++) {
//                    System.out.println("Prev = " + Integer.toBinaryString(prev));
//                    for (int curr = 0; curr < 1 << N; curr++) {
//                        System.out.println("Curr = " + Integer.toBinaryString(curr) + " gives " + dp[c][prev][curr] + " ");
//                    }
//                }
//            }
            out.println(N * M - ans);
        }
        out.close();
    }

    static int N, M;
    static int oo = 999;
    static int[] dr = {1, 0, -1, 0}, dc = {0, 1, 0, -1};

    static boolean isValid(int prev, int curr, int next) {
        boolean[][] grid = new boolean[N][3];
        int[] subsets = {prev, curr, next};
        for (int r = 0; r < N; r++) {
            for (int c = 0; c < 3; c++) {
                if ((subsets[c] & 1) > 0) {
                    grid[r][c] = true;
                    for (int k = 0; k < 4; k++) {
                        int r2 = r + dr[k];
                        int c2 = c + dc[k];
                        if (0 <= r2 && r2 <= N - 1 && 0 <= c2 && c2 <= 2) {
                            grid[r2][c2] = true;
                        }
                    }
                }
                subsets[c] >>= 1;
            }
        }
        for (int r = 0; r < N; r++) {
            if (!grid[r][1]) {
                // we only need to ensure the middle is safe
                return false;
            }
        }
        return true;
    }

    static class FastScanner {
        private int BS = 1 << 16;
        private char NC = (char) 0;
        private byte[] buf = new byte[BS];
        private int bId = 0, size = 0;
        private char c = NC;
        private double cnt = 1;
        private BufferedInputStream in;

        public FastScanner() {
            in = new BufferedInputStream(System.in, BS);
        }

        public FastScanner(String s) {
            try {
                in = new BufferedInputStream(new FileInputStream(new File(s)), BS);
            } catch (Exception e) {
                in = new BufferedInputStream(System.in, BS);
            }
        }

        private char getChar() {
            while (bId == size) {
                try {
                    size = in.read(buf);
                } catch (Exception e) {
                    return NC;
                }
                if (size == -1) return NC;
                bId = 0;
            }
            return (char) buf[bId++];
        }

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

        public int[] nextInts(int N) {
            int[] res = new int[N];
            for (int i = 0; i < N; i++) {
                res[i] = (int) nextLong();
            }
            return res;
        }

        public long[] nextLongs(int N) {
            long[] res = new long[N];
            for (int i = 0; i < N; i++) {
                res[i] = nextLong();
            }
            return res;
        }

        public long nextLong() {
            cnt = 1;
            boolean neg = false;
            if (c == NC) c = getChar();
            for (; (c < '0' || c > '9'); c = getChar()) {
                if (c == '-') neg = true;
            }
            long res = 0;
            for (; c >= '0' && c <= '9'; c = getChar()) {
                res = (res << 3) + (res << 1) + c - '0';
                cnt *= 10;
            }
            return neg ? -res : res;
        }

        public double nextDouble() {
            double cur = nextLong();
            return c != '.' ? cur : cur + nextLong() / cnt;
        }

        public double[] nextDoubles(int N) {
            double[] res = new double[N];
            for (int i = 0; i < N; i++) {
                res[i] = nextDouble();
            }
            return res;
        }

        public String next() {
            StringBuilder res = new StringBuilder();
            while (c <= 32) c = getChar();
            while (c > 32) {
                res.append(c);
                c = getChar();
            }
            return res.toString();
        }

        public String nextLine() {
            StringBuilder res = new StringBuilder();
            while (c <= 32) c = getChar();
            while (c != '\n') {
                res.append(c);
                c = getChar();
            }
            return res.toString();
        }

        public boolean hasNext() {
            if (c > 32) return true;
            while (true) {
                c = getChar();
                if (c == NC) return false;
                else if (c > 32) return true;
            }
        }
    }

    static void ASSERT(boolean assertion, String message) {
        if (!assertion) throw new AssertionError(message);
    }

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


/*(c) gorlum0 [at] gmail.com*/
import java.io.*;
import java.util.*;
import java.math.*;

public class E
{
    int bitcount(int x) {
        int c = 0;
        for ( ; x != 0; c++)
            x &= x-1;
        return c;
    }

    boolean bit(int x, int i)
    {
        if (i < 0) return false;
        return (x>>i & 1) == 1 ? true : false;
    }

    int solve(int n, int m)
    {
        if (m > n) { int x = m; m = n; n = x; }
        int maxmask = 1<<m;

        int[][][] dp = new int[n+1][maxmask][maxmask];
        for (int i = 0; i <= n; i++)
            for (int j = 0; j < maxmask; j++)
                for (int k = 0; k < maxmask; k++)
                    dp[i][j][k] = inf;
        for (int i = 0; i < maxmask; i++)
            dp[0][0][i] = bitcount(i);

        for (int i = 1; i <= n; i++)
            for (int b = 0; b < maxmask; b++)
                for (int c = 0; c < maxmask; c++)
                    for (int a = 0; a < maxmask; a++) {
                        boolean nospider = false;
                        for (int j = 0; j < m; j++)
                            if (not(bit(a,j) || bit(c,j) || bit(b,j-1) || bit(b,j) || bit(b,j+1))) {
                                nospider = true;
                                break;
                            }
                        if (nospider) continue;

                        dp[i][b][c] = Math.min(dp[i][b][c], dp[i-1][a][b] + bitcount(c));
                    }
        int res = inf;
        for (int b = 0; b < maxmask; b++)
            res = Math.min(res, dp[n][b][0]);
        return n*m - res;
    }

    void main() throws IOException {
        int n;
        while ((n = nextInt()) != EOF) {
            int m = nextInt();
            out.println(solve(n, m));
        }
    }

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

    // ======================================================================

    int inf = (int) 1e9;
    final int EOF = -1;

    boolean not(boolean p) { return !p; }

    int sqr(int x) { return x*x; }
    long sqr(long x) { return x*x; }
    double sqr(double x) { return x*x; }

    BufferedReader fin;
    StringTokenizer st;
    PrintWriter out;

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

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

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

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

    String nextToken() throws IOException {
        while (st == null || !st.hasMoreTokens()) {
            String line = fin.readLine();
            if (line == null) return "-1";
            else st = new StringTokenizer(line);
        }
        return st.nextToken();
    }
}


import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.lang.Math.*;
import static java.util.Arrays.*;
import static java.util.Collections.*;

// Petya and Spiders
// 2012/08/16
public class P111C{
	Scanner sc=new Scanner(System.in);

	int INF=1<<28;
	double EPS=1e-9;

	int h, w;

	void run(){
		h=sc.nextInt();
		w=sc.nextInt();
		solve();
	}

	void solve(){
		n=w*h;
		g=new long[n];
		int[] dx={0, 0, -1, 1};
		int[] dy={-1, 1, 0, 0};
		for(int y=0; y<h; y++){
			for(int x=0; x<w; x++){
				for(int k=0; k<4; k++){
					int x2=x+dx[k];
					int y2=y+dy[k];
					if(x2>=0&&x2<w&&y2>=0&&y2<h){
						g[y*w+x]|=1L<<(y2*w+x2);
					}
				}
			}
		}
		mds=(1L<<n)-1;
		mds(0, 0, 0);
		println((n-Long.bitCount(mds))+"");
	}

	int n;
	long[] g;
	long mds;

	void mds(long choosed, long removed, long covered){
		if(Long.bitCount(choosed)>=Long.bitCount(mds))
			return;

		if(covered==((1L<<n)-1)){
			mds=choosed;
			return;
		}

		long s=covered;
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			s|=(1L<<i)|g[i];
		}
		if(s!=((1L<<n)-1))
			return;

		int k=-1;
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			if((covered>>>i&1)==1){
				if(Long.bitCount(g[i]&~covered)==0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}else if(Long.bitCount(g[i]&~covered)==1
						&&(g[i]&~covered&~removed)!=0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}
			}else{
				if(Long.bitCount(g[i]&~removed)==0){
					mds(choosed|(1L<<i), removed|(1L<<i), covered|(1L<<i)|g[i]);
					return;
				}else if(Long.bitCount(g[i]&~removed)==1
						&&((g[i]&~removed)|(g[i]&~covered))==(g[i]&~removed)){
					int j=Long.numberOfTrailingZeros(g[i]&~removed);
					mds(choosed|(1L<<j), removed|(1L<<i)|(1L<<j), covered
							|(1L<<j)|g[j]);
					return;
				}
			}
			if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered))
				k=i;
		}
		if(k==-1)
			return;

		mds(choosed|(1L<<k), removed|(1L<<k), covered|(1L<<k)|g[k]);
		mds(choosed, removed|(1L<<k), covered);
	}

	void println(String s){
		System.out.println(s);
	}

	void debug(Object... os){
		System.err.println(Arrays.deepToString(os));
	}

	public static void main(String[] args){
		Locale.setDefault(Locale.US);
		new P111C().run();
	}

}


/*
If you want to aim high, aim high
Don't let that studying and grades consume you
Just live life young
******************************
If I'm the sun, you're the moon
Because when I go up, you go down
*******************************
I'm working for the day I will surpass you
https://www.a2oj.com/Ladder16.html
*/
import java.util.*;
import java.io.*;
import java.math.*;

   public class x111C
   {
      public static void main(String omkar[]) throws Exception
      {
         BufferedReader infile = new BufferedReader(new InputStreamReader(System.in));
         StringTokenizer st = new StringTokenizer(infile.readLine());
         int R = Integer.parseInt(st.nextToken());
         int C = Integer.parseInt(st.nextToken());
         if(R > C)
         {
            int t = R;
            R = C;
            C = t;
         }
         //dp[c][m1][m2] = min spoders in first c columns
         int[][][] dp = new int[C+1][1 << R][1 << R];
         for(int i=0; i <= C; i++)
            for(int mask=0; mask < (1<<R); mask++)
               Arrays.fill(dp[i][mask], 69);
         for(int mask=0; mask < (1<<R); mask++)
            dp[0][0][mask] = 0;
         for(int c=1; c <= C; c++)
            for(int mask1=0; mask1 < (1<<R); mask1++)
               for(int mask2=0; mask2 < (1<<R); mask2++)
                  for(int mask3=0; mask3 < (1<<R); mask3++)
                  {
                     boolean works = true;
                     for(int b=0; b < R; b++)
                        if((mask2&(1<<b)) == 0)
                        {
                           if(b > 0 && (mask2&(1<<(b-1))) > 0);
                           else if(b+1 < R && (mask2&(1<<(b+1))) > 0);
                           else if((mask1&(1<<b)) > 0);
                           else if((mask3&(1<<b)) > 0);
                           else  works = false;
                        }
                     if(works)
                        dp[c][mask2][mask3] = Math.min(dp[c][mask2][mask3], dp[c-1][mask1][mask2]+Integer.bitCount(mask1));
                  }
         int res = 0;
         for(int mask=0; mask < (1<<R); mask++)
            res = Math.max(res, R*C-(dp[C][mask][0]+Integer.bitCount(mask)));
         System.out.println(res);
      }
   }


// practice with rainboy
import java.io.*;
import java.util.*;

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

	int encode(int[] aa, int m) {
		int a = 0;
		for (int j = 0; j < m; j++)
			a = a * 3 + aa[j];
		return a;
	}
	void decode(int[] aa, int m, int a, int base) {
		for (int j = m - 1; j >= 0; j--) {
			aa[j] = a % base;
			a /= base;
		}
	}
	void main() {
		int n = sc.nextInt();
		int m = sc.nextInt();
		if (n < m) {
			int tmp = n; n = m; m = tmp;
		}
		int p = 1;
		for (int j = 0; j < m; j++)
			p *= 3;
		int[] dp = new int[p];
		int[] dq = new int[p];
		int[] aa = new int[m];
		int[] bb = new int[m];
		for (int j = 0; j < m; j++)
			aa[j] = 1;
		Arrays.fill(dp, -1);
		dp[encode(aa, m)] = 0;
		while (n-- > 0) {
			Arrays.fill(dq, -1);
			for (int a = 0; a < p; a++) {
				if (dp[a] < 0)
					continue;
				decode(aa, m, a, 3);
				for (int b = 0; b < 1 << m; b++) {
					decode(bb, m, b, 2);
					boolean bad = false;
					for (int j = 0; j < m; j++)
						if (aa[j] == 0 && bb[j] == 0) {
							bad = true;
							break;
						}
					if (bad)
						continue;
					int cnt = 0;
					for (int j = 0; j < m; j++)
						if (bb[j] == 1) {
							bb[j] = 2;
							cnt++;
						}
					for (int j = 0; j < m; j++)
						if (bb[j] == 0 && (aa[j] == 2 || j > 0 && bb[j - 1] == 2 || j + 1 < m && bb[j + 1] == 2))
							bb[j] = 1;
					int a_ = encode(bb, m);
					dq[a_] = Math.max(dq[a_], dp[a] + m - cnt);
				}
			}
			int[] tmp = dp; dp = dq; dq = tmp;
		}
		int ans = 0;
		for (int a = 0; a < p; a++) {
			if (dp[a] <= ans)
				continue;
			decode(aa, m, a, 3);
			boolean bad = false;
			for (int j = 0; j < m; j++)
				if (aa[j] == 0) {
					bad = true;
					break;
				}
			if (!bad)
				ans = dp[a];
		}
		println(ans);
	}
}


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

import static java.lang.Math.*;
import static java.util.Arrays.*;

public class C{
    Scanner sc=new Scanner(System.in);

    int INF=1<<28;
    double EPS=1e-9;

    int n, m;

    void run(){
        n=sc.nextInt();
        m=sc.nextInt();
        solve();
    }

    void solve(){
        if(n<m){
            int t=n;
            n=m;
            m=t;
        }
        int full=(1<<m)-1;
        int[][] dp=new int[1<<m][1<<m];
        int[][] tmp=new int[1<<m][1<<m];
        for(int i=0; i<1<<m; i++){
            fill(dp[i], INF);
        }
        for(int i=0; i<1<<m; i++){
            int b1=(i|(i>>1)|(i<<1))&full;
            int b2=i;
            dp[b1][b2]=Integer.bitCount(i);
            debug(Integer.toBinaryString(b1), dp[b1]);
        }
        debug();
        for(int j=0; j<n-1; j++){
            for(int i=0; i<1<<m; i++){
                System.arraycopy(dp[i], 0, tmp[i], 0, 1<<m);
                fill(dp[i], INF);
            }

            for(int b1=0; b1<1<<m; b1++){
                for(int b2=0; b2<1<<m; b2++){
                    for(int i=0; i<1<<m; i++){
                        if((b1|i)!=full){
                            continue;
                        }
                        if(false)
                            debug(Integer.toBinaryString(b1),
                                    Integer.toBinaryString(b2),
                                    Integer.toBinaryString(i));
                        int b=(i|(i>>1)|(i<<1))&full;
                        dp[b2|b][i]=min(dp[b2|b][i],
                                tmp[b1][b2]+Integer.bitCount(i));
                    }
                }
            }
            debug(j);
            for(int i=0; i<1<<m; i++){
                debug(Integer.toBinaryString(i), dp[i]);
            }
        }

        int min=INF;
        for(int i=0; i<1<<m; i++){
            min=min(min, dp[full][i]);
        }
        debug(min);
        int ans=m*n-min;
        debug("ans",ans);
        println(ans+"");
    }

    void println(String s){
        System.out.println(s);
    }

    void print(String s){
        System.out.print(s);
    }

    void debug(Object... os){
        // System.err.println(Arrays.deepToString(os));
    }

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


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import static java.lang.Math.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import java.util.TreeMap;

/**
 *
 * @author pttrung
 */
public class C {

    public static double Epsilon = 1e-6;
    public static long x, y, d;
    public static long MOD = 1000000007;
    public static int[][][] dp;
    public static int min, max, need;

    public static void main(String[] args) {

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

        int n = in.nextInt();
        int m = in.nextInt();
        min = Math.min(n, m);
        max = (m + n) - min;
        dp = new int[max][1 << min][1 << min];

        for (int[][] temp : dp) {
            for (int[] val : temp) {
                Arrays.fill(val, -1);
            }
        }
        need = (1 << min) - 1;
        //System.out.println(add + total);
        //  System.out.println(min + " " + max);

        out.println(cal(0, 0, 0));

        out.close();

    }

    public static int cal(int index, int last, int lastHold) {

        if (index == max) {
            return 0;
        }
        if (dp[index][lastHold][last] != -1) {
            return dp[index][lastHold][last];
        }
        int result = 0;

        for (int i = 0; i < 1 << min; i++) {
            if ((i | last) == need || (index == 0)) {
              //  System.out.println("PREV " + index + " " + i + " " + last + " " + lastHold);
              //  System.out.println("NEXT " + index + " " + i + " " + (i | lastHold) + " " + i);
                //   System.out.println(Integer.bitCount(i) + " " + i);

//                if (index == 3) {
//                    System.out.println(last + " " + i + " " + match(i, last) + " " + next);
//                }
                if(index + 1 == max && match(i,lastHold)!= need){
                    continue;
                }
                int temp = cal(index + 1, match(i,lastHold), i) + (min - Integer.bitCount(i));
                result = result < temp ? temp : result;

                //  break;
            }
        }
        dp[index][lastHold][last] = result;
        return result;
    }

    public static int match(int mask, int last) {
        int result = last;
        for (int i = 0; i < min; i++) {
            if (((1 << i) & mask) != 0) {
                int a = i - 1;
                int b = i + 1;
                result |= (1 << i);
                if (a >= 0) {
                    result |= (1 << a);
                }
                if (b < min) {
                    result |= (1 << b);
                }
            }
        }
        // System.out.println(mask + " " + result + " " + need);
        return result ;
    }

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

    public static void extendEuclid(long a, long b) {
        if (b == 0) {
            x = 1;
            y = 0;
            d = a;
            return;
        }
        extendEuclid(b, a % b);
        long x1 = y;
        long y1 = x - (a / b) * y;
        x = x1;
        y = y1;

    }

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

    static class Line {

        double a, b, c;

        Line(double x1, double y1, double x2, double y2) {
            if (Math.abs(x1 - x2) < Epsilon && Math.abs(y1 - y2) < Epsilon) {
                throw new RuntimeException("Two point are the same");
            }
            a = y1 - y2;
            b = x2 - x1;
            c = -a * x1 - b * y1;
        }

        Line(Point x, Point y) {
            this(x.x, x.y, y.x, y.y);
        }

        public Line perpendicular(Point p) {
            return new Line(p, new Point(p.x + a, p.y + b));
        }

        public Point intersect(Line l) {
            double d = a * l.b - b * l.a;
            if (d == 0) {
                throw new RuntimeException("Two lines are parallel");
            }

            return new Point((l.c * b - c * l.b) / d, (l.a * c - l.c * a) / d);
        }
    }

    static void check(Point a, Point b, ArrayList<Point> p, Point[] rec, int index) {
        for (int i = 0; i < 4; i++) {
            int m = (i + index) % 4;
            int j = (i + 1 + index) % 4;
            Point k = intersect(minus(b, a), minus(rec[m], rec[j]), minus(rec[m], a));
            if (k.x >= 0 && k.x <= 1 && k.y >= 0 && k.y <= 1) {
                Point val = new Point(k.x * minus(b, a).x, k.x * minus(b, a).y);
                p.add(add(val, a));
                // System.out.println(a + " " + b + " " + rec[i] + " " + rec[j]);
                // System.out.println(add(val, a));
            }
        }
    }

    static Point intersect(Point a, Point b, Point c) {
        double D = cross(a, b);
        if (D != 0) {
            return new Point(cross(c, b) / D, cross(a, c) / D);
        }
        return null;
    }

    static Point convert(Point a, double angle) {
        double x = a.x * cos(angle) - a.y * sin(angle);
        double y = a.x * sin(angle) + a.y * cos(angle);
        return new Point(x, y);
    }

    static Point minus(Point a, Point b) {
        return new Point(a.x - b.x, a.y - b.y);
    }

    static Point add(Point a, Point b) {
        return new Point(a.x + b.x, a.y + b.y);
    }

    static double cross(Point a, Point b) {
        return a.x * b.y - a.y * b.x;
    }

    static class Point {

        double x, y;

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

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

    static class Scanner {

        BufferedReader br;
        StringTokenizer st;

        public Scanner() {
            // System.setOut(new PrintStream(new BufferedOutputStream(System.out), true));
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        public String next() {

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

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

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

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

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

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


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

/**
 * Author -
 * User: kansal
 * Date: 9/3/11
 * Time: 5:28 PM
 */
public class CF85C {
    public static void main(String[] args) {
        reader = new BufferedReader(new InputStreamReader(System.in));

        int height = nextInt(), width = nextInt();
        if (width > height) {
            int t = width;
            width = height;
            height = t;
        }

        final int INF = height * width + 10;
        final int ALL_BITS = (1 << width)  - 1;
        int[][][] dp = new int[height + 1][1 << width][1 << width];
        for (int[][] ints : dp) {
            for (int[] anInt : ints) {
                Arrays.fill(anInt, INF);
            }
        }

        dp[0][0][0] = 0;
        for(int r = 0; r < height; ++r) {
            for(int uncovered = 0; uncovered < (1 << width); ++uncovered) {
                for(int mask = 0; mask < (1 << width); ++mask) {
                    if (dp[r][uncovered][mask] == INF) {
                        continue;
                    }
                    for(int curMask = uncovered; curMask < (1 << width); curMask = (curMask + 1) | uncovered) {
                        int curCovered = (mask | curMask);
                        curCovered |= (curMask >> 1);
                        curCovered |= (ALL_BITS & (curMask << 1));

                        int curUncovered = ALL_BITS ^ curCovered;
                        dp[r+1][curUncovered][curMask] = Math.min(dp[r+1][curUncovered][curMask], dp[r][uncovered][mask] + Integer.bitCount(curMask));
                    }
                }
            }
        }

        int res = INF;
        for(int x: dp[height][0]) res = Math.min(res, x);

        System.out.println(height * width - res);
    }

    private static boolean hasBit(int mask, int bit) {
        return (((mask >> bit) & 1) == 1);
    }

    public static BufferedReader reader;

    public static StringTokenizer tokenizer = null;

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

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

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

    static public String next() {
        return nextToken();
    }

    static public String nextLine() {
        try {
            return reader.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}


import java.util.*;

public class E
{
   public static void main(String[] args)
   {
      new E(new Scanner(System.in));
   }

   int N, M;
   int[][][] memo;

   int go(int i, int j, int mask)
   {
      if (i == N)
         return go(0, j+1, mask);
      if (j == M)
      {
         int mm = mask%(1<<N);
         //System.out.println(Integer.toBinaryString(mm)+" "+Integer.toBinaryString(mask));
         if (mm != ((1<<N)-1))
            return N*M;
         return 0;
      }

      if (memo[i][j][mask] != -1)
         return memo[i][j][mask];

      // Now place a spider cluster here
      int nMask = mask;
      int prevMask = 0;
      if (i > 0)
         prevMask = 1 << (N-1);
      int nextMask = 0;
      if (i < (N-1))
         nextMask = 1 << (N+1);
      int curMask = 1 << N;
      int nextRowMask = 1 << (N+N);
      nMask = nMask|prevMask|nextMask|curMask|nextRowMask;
      nMask = nMask/2;
      int res = 1+go(i+1, j, nMask);

      int pr = mask%2;
      if (pr == 1)
      {
         // We have the option to skip here
         int rr = go(i+1, j, mask/2);
         if (rr < res)
            res = rr;
      }

      //System.out.printf("%d %d %s = %d%n", i, j, Integer.toBinaryString(mask), res);
      memo[i][j][mask] = res;
      return res;
   }

   public E(Scanner in)
   {
      int[] vals = new int[2];
      vals[0] = in.nextInt();
      vals[1] = in.nextInt();
      Arrays.sort(vals);

      N = vals[0];
      M = vals[1];
      memo = new int[N][M][1<<(N+N+1)];
      fill3(memo, -1);

      int r1 = go(0, 0, (1<<N)-1);
      int res = N*M-r1;
      System.out.printf("%d%n", res);

   }

   void fill3(int[][][] vvv, int val)
   {
      for (int[][] vv : vvv)
         for (int[] v : vv)
            Arrays.fill(v, val);
   }
}


import static java.lang.Math.*;
import static java.lang.System.currentTimeMillis;
import static java.lang.System.exit;
import static java.lang.System.arraycopy;
import static java.util.Arrays.sort;
import static java.util.Arrays.binarySearch;
import static java.util.Arrays.fill;
import java.util.*;
import java.io.*;

public class Main {

	public static void main(String[] args) throws IOException {
		try {
			if (new File("input.txt").exists())
				System.setIn(new FileInputStream("input.txt"));
		} catch (SecurityException e) {
		}
		new Thread(null, new Runnable() {
			public void run() {
				try {
					new Main().run();
				} catch (Throwable e) {
					e.printStackTrace();
					exit(999);
				}
			}
		}, "1", 1 << 23).start();
	}

	BufferedReader in;
	PrintWriter out;
	StringTokenizer st = new StringTokenizer("");

	int n, m;
	int dp[][][];
	int MV = Integer.MAX_VALUE >> 1;
	int ans = MV;

	private void run() throws IOException {
		in = new BufferedReader(new InputStreamReader(System.in));
		out = new PrintWriter(System.out);

		n = nextInt();
		m = nextInt();
		if(n < m){ int d = n; n = m; m = d; }
		int M = 1 << m;
		dp = new int[n][M][M];
		for(int a[][] : dp)
			for(int b[] : a)
				fill(b, MV);

//		for(int i = 0 ; i < M; i++){
//			int next = (( i | (i << 1) | (i >> 1) ) & (M - 1));
//			if(  next!= (M -1))
//				continue;
//			if(n > 1)
//				dp[0][next][0] = Integer.bitCount(i);
//			else
//				ans = min(ans, Integer.bitCount(i));
//		}
		dp[0][0][0] = 0;

		for(int i = 0; i < n; i++)
			for(int m1 = 0; m1 < M; m1++)
				for(int m2 = 0; m2 < M; m2++){
					if(dp[i][m1][m2] == MV)
						continue;
					for(int nm1 = 0; nm1 < M; nm1++)
						for(int nm2 = 0; nm2 < M; nm2++){
							int res1 = m1 | (nm1) | (nm1 << 1) | (nm1 >> 1) | (nm2);
							res1 &= (M - 1);

							if(res1 != (M - 1))
								continue;

							int res2 = m2 | (nm1) | (nm2 << 1) | (nm2 >> 1) | (nm2);
							res2 &= (M - 1);

//							if(i == 2 && m1 == 3 && m2 == 2 && nm1 == 0 && nm2 == 0){
//								System.err.println("kek");
//								System.err.println(dp[i][m1][m2]);
//								System.err.println(res1 + " | " + res2);
//								System.err.println(M);
//							}

							int next1 = res2 & (M - 1);
							int next2 = nm2 & ( M - 1);
							int over = Integer.bitCount(nm1) + Integer.bitCount(nm2);

							if(i < n - 1)
								dp[i+1][next1][next2] = min(dp[i + 1][next1][next2], dp[i][m1][m2] + over);
							else
								if((res1 & (M - 1)) == (M - 1)){
									ans = min(dp[i][m1][m2] + over, ans);
								}
						}

				}
//		System.err.println(ans);
		out.println(n * m - ans);

		in.close();
		out.close();
	}
	String nextToken() throws IOException {
		while (!st.hasMoreTokens())
			st = new StringTokenizer(in.readLine());
		return st.nextToken();
	}
	int nextInt() throws IOException {
		return Integer.parseInt(nextToken());
	}
	long nextLong() throws IOException {
		return Long.parseLong(nextToken());
	}
	double nextDouble() throws IOException {
		return Double.parseDouble(nextToken());
	}
	String nextLine() throws IOException {
		st = new StringTokenizer("");
		return in.readLine();
	}
	boolean EOF() throws IOException {
		while (!st.hasMoreTokens()) {
			String s = in.readLine();
			if (s == null)
				return true;
			st = new StringTokenizer(s);
		}
		return false;
	}
}


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

import static java.lang.Math.*;
import static java.util.Arrays.*;

// Petya and Spiders
// 2011/9/3
public class P111C{
	Scanner sc=new Scanner(System.in);

	int INF=1<<28;

	int n, m;

	void run(){
		n=sc.nextInt();
		m=sc.nextInt();
		solve();
	}

	void solve(){
		if(n<m){
			int t=n;
			n=m;
			m=t;
		}
		int full=(1<<m)-1;
		int[][] dp=new int[1<<m][1<<m];
		int[][] tmp=new int[1<<m][1<<m];
		for(int i=0; i<1<<m; i++){
			fill(dp[i], INF);
		}
		for(int i=0; i<1<<m; i++){
			int b1=(i|(i>>1)|(i<<1))&full;
			int b2=i;
			dp[b1][b2]=Integer.bitCount(i);
		}
		for(int j=0; j<n-1; j++){
			for(int i=0; i<1<<m; i++){
				System.arraycopy(dp[i], 0, tmp[i], 0, 1<<m);
				fill(dp[i], INF);
			}

			for(int b1=0; b1<1<<m; b1++){
				for(int b2=0; b2<1<<m; b2++){
					for(int i=0; i<1<<m; i++){
						if((b1|i)!=full){
							continue;
						}
						int b=(i|(i>>1)|(i<<1))&full;
						dp[b2|b][i]=min(dp[b2|b][i],
								tmp[b1][b2]+Integer.bitCount(i));
					}
				}
			}
		}

		int min=INF;
		for(int i=0; i<1<<m; i++){
			min=min(min, dp[full][i]);
		}
		int ans=m*n-min;
		println(ans+"");
	}

	void println(String s){
		System.out.println(s);
	}

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


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

/**
 * Author -
 * User: kansal
 * Date: 9/3/11
 * Time: 5:28 PM
 */
public class CF85E {
    public static void main(String[] args) {
        reader = new BufferedReader(new InputStreamReader(System.in));

        int height = nextInt(), width = nextInt();
        if (width > height) {
            int t = width;
            width = height;
            height = t;
        }

        final int INF = height * width + 10;
        int[][][] dp = new int[height + 1][1 << width][1 << width];
        for (int[][] ints : dp) {
            for (int[] anInt : ints) {
                Arrays.fill(anInt, INF);
            }
        }

        dp[0][0][0] = 0;
        for(int r = 0; r < height; ++r) {
            for(int uncovered = 0; uncovered < (1 << width); ++uncovered) {
                for(int mask = 0; mask < (1 << width); ++mask) {
                    if (dp[r][uncovered][mask] == INF) {
                        continue;
                    }
                    for(int curMask = uncovered; curMask < (1 << width); curMask = (curMask + 1) | uncovered) {
                        int curUncovered = (1 << width) - 1;
                        for(int i = 0; i < width; ++i) {
                            if (hasBit(mask, i) || hasBit(curMask, i)) {
                                curUncovered &= ~(1 << i);
                            }
                            if (i > 0 && hasBit(curMask, i-1)) {
                                curUncovered &= ~(1 << i);
                            }
                            if (i < width-1 && hasBit(curMask, i+1)) {
                                curUncovered &= ~(1 << i);
                            }
                        }
                        dp[r+1][curUncovered][curMask] = Math.min(dp[r+1][curUncovered][curMask], dp[r][uncovered][mask] + Integer.bitCount(curMask));
                    }
                }
            }
        }

        int res = INF;
        for(int x: dp[height][0]) res = Math.min(res, x);

        System.out.println(height * width - res);
    }

    private static boolean hasBit(int mask, int bit) {
        return (((mask >> bit) & 1) == 1);
    }

    public static BufferedReader reader;

    public static StringTokenizer tokenizer = null;

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

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

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

    static public String next() {
        return nextToken();
    }

    static public String nextLine() {
        try {
            return reader.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}