#include <bits/stdc++.h>
//上記が使用不可な環境において
/*
#include <iostream> // cout, endl, cin
#include <string> // string, to_string, stoi
#include <iomanip>  //setprecsion
#include <vector> // vector
#include <algorithm> // min, max, swap, sort, reverse, lower_bound, upper_bound
#include <utility> // pair, make_pair
#include <cmath>  //sin, cos, tan
#include <tuple> // tuple, make_tuple
#include <cstdint> // int64_t, int*_t
#include <cstdio> // printf
#include <map> // map
#include <queue> // queue, priority_queue
#include <set> // set
#include <stack> // stack
#include <deque> // deque
#include <unordered_map> // unordered_map
#include <unordered_set> // unordered_set
#include <bitset> // bitset
#include <cctype> // isupper, islower, isdigit, toupper, tolower
#include <cstdlib>
#include <cstring>
#include <numeric>
#include <complex>
#include <bitset>
#include <utility>
#include <functional>
*/
using namespace std;

typedef long long int ll;
typedef long long int lli;
typedef unsigned long long int ull;
typedef long double ld;
typedef string str;
typedef vector<char> vc;
typedef vector<bool> vb;
typedef vector<ld> vd;
typedef vector<str> vs;
typedef vector<ll> vll;
typedef vector<pair<ll, ll>> vpll;
typedef vector<vector<ll>> vvll;
typedef vector<vector<char>> vvc;
typedef vector<vector<str>> vvs;
const ld PI = acos(-1.0);
const ll MAX = 9000000000000000000;
const ll MIN = -9000000000000000000;
const ld DMAX = 4500;
const ld DMIN = -4500;

//謎
const int INF = INT_MAX;
const ll LLINF = 1LL << 60;
const ll MOD = 1000000007;
const ld EPS = 1e-9;
ll dx[8] = {1, 0, -1, 0, 1, -1, -1, 1};
ll dy[8] = {0, 1, 0, -1, 1, 1, -1, -1};

//0~nの範囲操作
#define rep(i, n) for (ll i = 0; i < (n); ++i)
//1~nの範囲操作
#define rrep(i, n) for (ll i = 1; i <= (n); ++i)
#define irep(it, stl) for (auto it = stl.begin(); it != stl.end(); it++)
#define drep(i, n) for (ll i = (n)-1; i >= 0; --i)
//出力後改行を
#define fin(ans) cout << (ans) << endl;
//出力後空白を
#define blank(ans) cout << (ans) << " ";
#define mp(p, q) make_pair(p, q)
#define pb(n) push_back(n)
//配列全操作
#define all(a) a.begin(), a.end()

//配列の合計
template <typename T>
T Sum(vector<T> n, ll a = 0, ll b = 0)
{
  if (b == 0)
  {
    return (accumulate(n.begin() + a, n.end(), 0));
  }
  else
  {
    return (accumulate(n.begin() + a, n.begin() + b, 0));
  }
}
template <>
ld Sum(vector<ld> n, ll a, ll b)
{
  if (b == 0)
  {
    return (accumulate(n.begin() + a, n.end(), 0));
  }
  else
  {
    return (accumulate(n.begin() + a, n.begin() + b, 0));
  }
}
//配列を昇順
template <typename T>
void Sort(T &n, ll a = 0, ll b = 0)
{
  if (b == 0)
  {
    return (sort(n.begin() + a, n.end()));
  }
  else
  {
    return (sort(n.begin() + a, n.begin() + b));
  }
}
//配列を降順
template <typename T>
void Reve(T &n, ll a = 0, ll b = 0)
{
  if (b == 0)
  {
    return (reverse(n.begin() + a, n.end()));
  }
  else
  {
    return (reverse(n.begin() + a, n.begin() + b));
  }
}
//配列nをmにコピー
template <typename T, typename U>
void Copy(T &n, U &m, ll a = 0, ll b = 0)
{
  copy(all(n), m.begin());
}
//小数点型に
template <typename T>
ld Cast(T n)
{
  return static_cast<ld>(n);
}
//配列入力
template <typename T>
void V(T &n)
{
  for (ll i = 0; i < n.size(); i++)
  {
    cin >> n[i];
  }
}
//２列配列入力
template <typename T, typename U>
void V2(T &n, U &m)
{
  for (ll i = 0; i < n.size(); i++)
  {
    cin >> n[i] >> m[i];
  }
}
//３列配列入力
template <typename T, typename U, typename W>
void V3(T &n, U &m, W &p)
{
  for (ll i = 0; i < n.size(); i++)
  {
    cin >> n[i] >> m[i] >> p[i];
  }
}
//yes, noと表示
void yn(bool a)
{
  if (a)
    cout << "yes" << endl;
  else
    cout << "no" << endl;
}
//Yes, Noと表示
void Yn(bool a)
{
  if (a)
    cout << "Yes" << endl;
  else
    cout << "No" << endl;
}
//YES, NOと表示
void YN(bool a)
{
  if (a)
    cout << "YES" << endl;
  else
    cout << "NO" << endl;
}
//yes, noと表示後終了
void fyn(bool a)
{
  if (a)
    cout << "yes" << endl;
  else
    cout << "no" << endl;
  exit(0);
}
//Yes, Noと表示後終了
void fYn(bool a)
{
  if (a)
    cout << "Yes" << endl;
  else
    cout << "No" << endl;
  exit(0);
}
//YES, NOと表示後終了
void fYN(bool a)
{
  if (a)
    cout << "YES" << endl;
  else
    cout << "NO" << endl;
  exit(0);
}
//AC, WAと表示
void AC(bool a)
{
  if (a)
    cout << "AC" << endl;
  else
    cout << "WA" << endl;
}
//AC, WAと表示後終了
void fAC(bool a)
{
  if (a)
    cout << "AC" << endl;
  else
    cout << "WA" << endl;
  exit(0);
}
//noneと表示後終了
void fnone(void)
{
  cout << "None" << endl;
  exit(0);
}
//Odd(奇数), Even(偶数)と表示後終了
void fOdd(bool a)
{
  if (a)
    cout << "Odd" << endl;
  else
    cout << "Even" << endl;
  exit(0);
}
//Possible, Impossibleと表示後終了
void Possible(bool a)
{
  if (a)
    cout << "Possible" << endl;
  else
    cout << "Impossible" << endl;
  exit(0);
}
//POSSIBLE, IMPOSSIBLEと表示後終了
void POSSIBLE(bool a)
{
  if (a)
    cout << "POSSIBLE" << endl;
  else
    cout << "IMPOSSIBLE" << endl;
  exit(0);
}
//四角　表示文字 a 縦幅 h 横幅 w
void square(char a, ll h, ll w)
{
  for (ll i = 0; i < h; i++)
  {
    for (ll j = 0; j < w; j++)
    {
      cout << a;
    }
    cout << endl;
  }
}
//四角枠　外周 a 中身 b 縦幅 h 横幅 w
void yoke(char a, char b, ll h, ll w)
{
  for (ll i = 0; i < h; i++)
  {
    for (ll j = 0; j < w; j++)
    {
      if (i == 0 || i == h - 1)
        cout << a;
      else if (j == 0 || j == w - 1)
        cout << a;
      else
        cout << b;
    }
    cout << endl;
  }
}
//四捨五入
ll C45(ll n)
{
  return (n + 1) / 2;
}
//文字列からcを探す
ll stcount(str s, char c)
{
  return count(s.cbegin(), s.cend(), c);
}
//素数
bool IsPrime(ll num)
{
  if (num < 2)
    return false;
  else if (num == 2)
    return true;
  else if (num % 2 == 0)
    return false;
  ld sqrtNum = sqrt(num);
  for (ll i = 3; i <= sqrtNum; i += 2)
  {
    if (num % i == 0)
      return false;
  }
  return true;
}
//桁数
ll GetDigit(ll num)
{
  return log10(num) + 1;
}
//各桁の和
ll KSum(ll n)
{
  ll sum = 0;
  if (n < 0)
    return 0;
  while (n > 0)
  {
    sum += n % 10;
    n /= 10;
  }
  return sum;
}
//数値反転
bool KReve(ll n)
{
  int reverse = 0;
  int remaind;
  int tmp = n;
  while (tmp != 0)
  {
    remaind = tmp % 10;
    reverse = reverse * 10 + remaind;
    tmp /= 10;
  }
  if (reverse == n)
    return true;
  else
    return false;
}
//約数全列挙
vector<ll> enum_div(ll n)
{
  vector<ll> ret;
  for (ll i = 1; i * i <= n; ++i)
  {
    if (n % i == 0)
    {
      ret.push_back(i);
      if (i != 1 && i * i != n)
      {
        ret.push_back(n / i);
      }
    }
  }
  return ret;
}
//最大公約数
ll gcd(ll a, ll b)
{
  return b ? gcd(b, a % b) : a;
}
//複数個の最大公約数
ll ngcd(vector<ll> a)
{
  ll res;
  res = a[0];
  for (ll i = 1; i < a.size() && res != 1; i++)
  {
    res = gcd(a[i], res);
  }
  return res;
}
//最小公倍数
ll lcm(ll a, ll b)
{
  return a * b / gcd(a, b);
}
//複数個の最小公倍数
ll nlcm(vector<ll> numbers)
{
  ll res;
  res = numbers[0];
  for (ll i = 1; i < numbers.size(); i++)
  {
    res = lcm(res, numbers[i]);
  }
  return res;
}
//累乗
ll Pow(ll x, ll n)
{
  ll res = 1;
  rep(i, n) res = res * x;
  return res;
}
//累乗(xのn乗%mod)
ll mod_pow(ll x, ll n, ll mod = MAX)
{
  ll res = 1;
  while (n > 0)
  {
    if (n & 1)
      res = res * x % mod;
    x = x * x % mod;
    n >>= 1;
  }
  return res;
}
//階乗
ll factorial(ll n)
{
  if (n > 0)
    return n * factorial(n - 1);
  else
    return 1;
}
//順列 nPr
ll perm(ll a, ll b)
{
  return (factorial(a) / factorial(a - b));
}
//組み合わせ　nCr
ll comb(ll a, ll b)
{
  return (factorial(a) / (factorial(a - b) * factorial(b)));
}
//n ~ mの和
ll sigma(ll n, ll m)
{
  return ((n + m) * (m - n + 1) * 0.5);
}
//期待値
ld hope(ld a)
{
  return (sigma(1, a) / a);
}
//謎
template <class T>
inline bool chmax(T &a, T b)
{
  if (a < b)
  {
    a = b;
    return 1;
  }
  return 0;
}
template <class T>
inline bool chmin(T &a, T b)
{
  if (a > b)
  {
    a = b;
    return 1;
  }
  return 0;
}
/*
//幅優先探索
// 各座標に格納したい情報を構造体にする
// 今回はX座標,Y座標,深さ(距離)を記述している
struct Corr
{
  ll x;
  ll y;
  ll depth;
};
queue<Corr> q;
ll bfs(vector<vector<ll>> grid)
{
  // 既に探索の場所を1，探索していなかったら0を格納する配列
  vector<vector<ll>> ispassed(grid.size(), vector<ll>(grid[0].size(), false));
  // このような記述をしておくと，この後のfor文が綺麗にかける
  ll dx[8] = {1, 0, -1, 0};
  ll dy[8] = {0, 1, 0, -1};
  while (!q.empty())
  {
    Corr now = q.front();
    q.pop();
    //今いる座標は(x,y)=(now.x, now.y)で，深さ(距離)はnow.depthである  ここで，今いる座標がゴール(探索対象)なのか判定する
    for (ll i = 0; i < 4; i++)
    {
      ll nextx = now.x + dx[i];
      ll nexty = now.y + dy[i];

      // 次に探索する場所のX座標がはみ出した時
      if (nextx >= grid[0].size())
        continue;
      if (nextx < 0)
        continue;

      // 次に探索する場所のY座標がはみ出した時
      if (nexty >= grid.size())
        continue;
      if (nexty < 0)
        continue;

      // 次に探索する場所が既に探索済みの場合
      if (ispassed[nexty][nextx])
        continue;

      ispassed[nexty][nextx] = true;
      Corr next = {nextx, nexty, now.depth + 1};
      q.push(next);
    }
  }
}
*/
//謎
//セグメント木
class Monoid
{
public:
  // 単位元
  ll unit;

  Monoid()
  {
    // 単位元
    unit = 0;
  }

  // 演算関数
  ll calc(ll a, ll b)
  {
    return a + b;
  }
};

class SegmentTree
{
public:
  // セグメント木の葉の要素数
  ll n;

  // セグメント木
  vector<ll> tree;

  // モノイド
  Monoid mono;

  SegmentTree(vector<ll> &v)
  {
    n = 1 << (ll)ceil(log2(v.size()));
    tree = vector<ll>(n << 1);
    for (ll i = 0; i < v.size(); i++)
    {
      update(i, v[i]);
    }
    for (ll i = v.size(); i < n; i++)
    {
      update(i, mono.unit);
    }
  }

  // k番目の値(0-indexed)をxに変更
  void update(ll k, ll x)
  {
    k += n;
    tree[k] = x;
    for (k = k >> 1; k > 0; k >>= 1)
    {
      tree[k] = mono.calc(tree[k << 1 | 0], tree[k << 1 | 1]);
    }
  }

  // [l, r)の最小値(0-indexed)を求める．
  ll query(ll l, ll r)
  {
    ll res = mono.unit;
    l += n;
    r += n;
    while (l < r)
    {
      if (l & 1)
      {
        res = mono.calc(res, tree[l++]);
      }
      if (r & 1)
      {
        res = mono.calc(res, tree[--r]);
      }
      l >>= 1;
      r >>= 1;
    }
    return res;
  }
  ll operator[](ll k)
  {
    // st[i]で添字iの要素の値を返す
    if (k - n >= 0 || k < 0)
    {
      return -INF;
    }
    return tree[tree.size() - n + k];
  }

  void show()
  {
    int ret = 2;
    for (ll i = 1; i < 2 * n; i++)
    {
      cout << tree[i] << " ";
      if (i == ret - 1)
      {
        cout << endl;
        ret <<= 1;
      }
    }
    cout << endl;
  }
};
//点と線の最短距離
struct Point
{
  ld x;
  ld y;
};

struct LineSegment
{
  Point start;
  Point end;
};

/**
   @brief 線分と点の距離を計算する。
   @param line 線分
   @param point 点
   @return ユークリッド距離
 */
ld calc_dist(const LineSegment &line, const Point &point)
{
  ld x0 = point.x, y0 = point.y;
  ld x1 = line.start.x, y1 = line.start.y;
  ld x2 = line.end.x, y2 = line.end.y;

  ld a = x2 - x1;
  ld b = y2 - y1;
  ld a2 = a * a;
  ld b2 = b * b;
  ld r2 = a2 + b2;
  ld tt = -(a * (x1 - x0) + b * (y1 - y0));

  if (tt < 0)
    return sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
  else if (tt > r2)
    return sqrt((x2 - x0) * (x2 - x0) + (y2 - y0) * (y2 - y0));

  ld f1 = a * (y1 - y0) - b * (x1 - x0);
  return sqrt((f1 * f1) / r2);
}
//文字を数字に
ll Moji(char a)
{
  return (a - 48);
}

//大文字->小文字(+32)

void Main()
{
  ll h, w; cin >> h >> w;
  if(h == 1||w == 1) {fin(1)}
  else if(h * w % 2 == 1) fin(h * w / 2 + 1)
  else fin(h * w / 2)
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout << fixed << setprecision(20);
  Main();
  return 0;
}
/*
//test時
g++ test.cpp -o a;./a
*/


Measured memory_bytes: 256