using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Numerics;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Codeforces.TaskC
{
    public class Task
    {
        void Solve()
        {
            Input.Next(out int n, out int m);
            var costs = Input.ArrayInt();
            var fd = new Dictionary<string, int>();
            for (int i = 0; i < m; i++)
            {
                Input.Next(out string fruit);

                if (fd.ContainsKey(fruit))
                    fd[fruit]++;
                else
                    fd[fruit] = 1;
            }

            int uniqueFruits = fd.Keys.Count;
            var descFruitCounts = fd.Values.OrderByDescending(i => i).ToList();

            var ascCosts = costs.OrderBy(i => i).ToList();
            var descCosts = costs.OrderByDescending(i => i).ToList();

            int a = 0,
                b = 0;

            for (int i = 0; i < uniqueFruits; i++)
            {
                a += ascCosts[i] * descFruitCounts[i];
                b += descCosts[i] * descFruitCounts[i];
            }

            Console.WriteLine($"{a} {b}");
        }

        public static void Main()
        {
            var task = new Task();
            #if DEBUG
            task.Solve();
            #else
            try
            {
                task.Solve();
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine(ex);
            }
            #endif
        }
    }
}

namespace Codeforces
{
    public class Input
    {
        private static string _line;

        public static bool Next()
        {
            _line = Console.ReadLine();
            return _line != null;
        }

        public static bool Next(out long a)
        {
            var ok = Next();
            a = ok ? long.Parse(_line) : 0;
            return ok;
        }

        public static bool Next(out long a, out long b)
        {
            var ok = Next();
            if (ok)
            {
                var array = _line.Split(' ').Select(long.Parse).ToArray();
                a = array[0];
                b = array[1];
            }
            else
            {
                a = b = 0;
            }

            return ok;
        }

        public static bool Next(out long a, out long b, out long c)
        {
            var ok = Next();
            if (ok)
            {
                var array = _line.Split(' ').Select(long.Parse).ToArray();
                a = array[0];
                b = array[1];
                c = array[2];
            }
            else
            {
                a = b = c = 0;
            }
            return ok;
        }

        public static bool Next(out long a, out long b, out long c, out long d)
        {
            var ok = Next();
            if (ok)
            {
                var array = _line.Split(' ').Select(long.Parse).ToArray();
                a = array[0];
                b = array[1];
                c = array[2];
                d = array[3];
            }
            else
            {
                a = b = c = d = 0;
            }
            return ok;
        }

        public static bool Next(out long a, out long b, out long c, out long d, out long e)
        {
            var ok = Next();
            if (ok)
            {
                var array = _line.Split(' ').Select(long.Parse).ToArray();
                a = array[0];
                b = array[1];
                c = array[2];
                d = array[3];
                e = array[4];
            }
            else
            {
                a = b = c = d = e = 0;
            }
            return ok;
        }

        public static bool Next(out int a)
        {
            var ok = Next();
            a = ok ? int.Parse(_line) : 0;
            return ok;
        }

        public static bool Next(out int a, out int b)
        {
            var ok = Next();
            if (ok)
            {
                var array = _line.Split(' ').Select(int.Parse).ToArray();
                a = array[0];
                b = array[1];
            }
            else
            {
                a = b = 0;
            }

            return ok;
        }

        public static bool Next(out int a, out int b, out int c)
        {
            var ok = Next();
            if (ok)
            {
                var array = _line.Split(' ').Select(int.Parse).ToArray();
                a = array[0];
                b = array[1];
                c = array[2];
            }
            else
            {
                a = b = c = 0;
            }
            return ok;
        }

        public static bool Next(out int a, out int b, out int c, out int d)
        {
            var ok = Next();
            if (ok)
            {
                var array = _line.Split(' ').Select(int.Parse).ToArray();
                a = array[0];
                b = array[1];
                c = array[2];
                d = array[3];
            }
            else
            {
                a = b = c = d = 0;
            }
            return ok;
        }

        public static bool Next(out int a, out int b, out int c, out int d, out int e)
        {
            var ok = Next();
            if (ok)
            {
                var array = _line.Split(' ').Select(int.Parse).ToArray();
                a = array[0];
                b = array[1];
                c = array[2];
                d = array[3];
                e = array[4];
            }
            else
            {
                a = b = c = d = e = 0;
            }
            return ok;
        }

        public static bool Next(out int a, out int b, out int c, out int d, out int e, out int f)
        {
            var ok = Next();
            if (ok)
            {
                var array = _line.Split(' ').Select(int.Parse).ToArray();
                a = array[0];
                b = array[1];
                c = array[2];
                d = array[3];
                e = array[4];
                f = array[5];
            }
            else
            {
                a = b = c = d = e = f = 0;
            }
            return ok;
        }

        public static IEnumerable<long> ArrayLong()
        {
            return !Next() ? new List<long>() : _line.Split().Select(long.Parse);
        }

        public static IEnumerable<int> ArrayInt()
        {
            return !Next() ? new List<int>() : _line.Split().Select(int.Parse);
        }

        public static bool Next(out string value)
        {
            value = string.Empty;
            if (!Next()) return false;
            value = _line;
            return true;
        }
    }

    public class DisjointSet
    {
        private readonly int[] _parent;
        private readonly int[] _rank;
        public int Count { get; private set; }

        public DisjointSet(int count, int initialize = 0)
        {
            Count = count;
            _parent = new int[Count];
            _rank = new int[Count];
            for (var i = 0; i < initialize; i++) _parent[i] = i;
        }

        private DisjointSet(int count, int[] parent, int[] rank)
        {
            Count = count;
            _parent = parent;
            _rank = rank;
        }

        public void Add(int v)
        {
            _parent[v] = v;
            _rank[v] = 0;
        }

        public int Find_Recursive(int v)
        {
            if (_parent[v] == v) return v;
            return _parent[v] = Find(_parent[v]);
        }

        public int Find(int v)
        {
            if (_parent[v] == v) return v;
            var last = v;
            while (_parent[last] != last) last = _parent[last];
            while (_parent[v] != v)
            {
                var t = _parent[v];
                _parent[v] = last;
                v = t;
            }
            return last;
        }

        public int this[int v]
        {
            get { return Find(v); }
            set { Union(v, value); }
        }

        public void Union(int a, int b)
        {
            a = Find(a);
            b = Find(b);
            if (a == b) return;
            if (_rank[a] < _rank[b])
            {
                var t = _rank[a];
                _rank[a] = _rank[b];
                _rank[b] = t;
            }
            _parent[b] = a;
            if (_rank[a] == _rank[b]) _rank[a]++;
        }

        public int GetSetCount()
        {
            var result = 0;
            for (var i = 0; i < Count; i++)
            {
                if (_parent[i] == i) result++;
            }
            return result;
        }

        public DisjointSet Clone()
        {
            var rank = new int[Count];
            _rank.CopyTo(rank, 0);
            var parent = new int[Count];
            _parent.CopyTo(parent, 0);
            return new DisjointSet(Count, parent, rank);
        }

        public override string ToString()
        {
            return string.Join(",", _parent.Take(50));
        }
    }

    public class Matrix
    {
        public static Matrix Create(int i, int j)
        {
            var m = new Matrix(i, j);
            return m;
        }

        public static Matrix Create(int i)
        {
            var m = new Matrix(i, i);
            return m;
        }

        public long Modulo { get; set; }

        private readonly int _width;
        private readonly int _height;
        private readonly long[,] _data;
        public int Width { get { return _width; } }
        public int Height { get { return _height; } }

        private Matrix(int i, int j)
        {
            Modulo = 1000000000000000000L;
            _width = j;
            _height = i;
            _data = new long[_height, _width];
        }

        public static Matrix operator *(Matrix m1, Matrix m2)
        {
            if (m1.Width != m2.Height) throw new InvalidDataException("m1.Width != m2.Height");
            var m = Create(m2.Width, m1.Height);
            m.Modulo = m1.Modulo;
            for (var i = 0; i < m2.Width; i++)
                for (var j = 0; j < m1.Height; j++)
                {
                    for (var k = 0; k < m1.Width; k++)
                        m[j, i] += (m1[j, k] * m2[k, i]) % m1.Modulo;
                    m[j, i] %= m1.Modulo;
                }
            return m;
        }

        public static Matrix operator +(Matrix m1, Matrix m2)
        {
            var m = m1.Clone();
            for (var i = 0; i < m2.Width; i++)
                for (var j = 0; j < m1.Height; j++)
                    m[j, i] = (m[j, i] + m2[j, i]) % m1.Modulo;
            return m;
        }

        public static Matrix operator -(Matrix m1, Matrix m2)
        {
            var m = m1.Clone();
            for (var i = 0; i < m2.Width; i++)
                for (var j = 0; j < m1.Height; j++)
                    m[j, i] = (m[j, i] - m2[j, i]) % m1.Modulo;
            return m;
        }

        public static Matrix operator *(Matrix m1, long l)
        {
            var m = m1.Clone();
            for (var i = 0; i < m1.Width; i++)
                for (var j = 0; j < m1.Height; j++)
                    m[j, i] = (m[j, i] * l) % m.Modulo;
            return m;
        }

        public static Matrix operator *(long l, Matrix m1)
        {
            return m1 * l;
        }

        public static Matrix operator +(Matrix m1, long l)
        {
            var m = m1.Clone();
            for (var i = 0; i < m1.Width; i++)
                m[i, i] = (m[i, i] + l) % m.Modulo;
            return m;
        }

        public static Matrix operator +(long l, Matrix m1)
        {
            return m1 + l;
        }

        public static Matrix operator -(Matrix m1, long l)
        {
            return m1 + (-l);
        }

        public static Matrix operator -(long l, Matrix m1)
        {
            var m = m1.Clone() * -1;
            return m + l;
        }

        public Matrix BinPower(long l)
        {
            var n = 1;
            var m = Clone();
            var result = new Matrix(m.Height, m.Width) + 1;
            result.Modulo = m.Modulo;
            while (l != 0)
            {
                var i = l & ~(l - 1);
                l -= i;
                while (n < i)
                {
                    m = m * m;
                    n <<= 1;
                }
                result *= m;
            }
            return result;
        }

        public void Fill(long l)
        {
            l %= Modulo;
            for (var i = 0; i < _height; i++)
                for (var j = 0; j < _width; j++)
                    _data[i, j] = l;
        }

        public Matrix Clone()
        {
            var m = new Matrix(_width, _height);
            Array.Copy(_data, m._data, _data.Length);
            m.Modulo = Modulo;
            return m;
        }

        public long this[int i, int j]
        {
            get { return _data[i, j]; }
            set { _data[i, j] = value % Modulo; }
        }
    }

    public class RedBlackTree<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
    {
        private readonly List<Node> _items;
        private Node _root;
        private readonly IComparer<TKey> _comparer;

        public RedBlackTree(int capacity = 10)
        {
            _items = new List<Node>(capacity);
            _comparer = Comparer<TKey>.Default;
        }

        private Node _search(TKey key)
        {
            var node = _root;
            while (node != null)
            {
                var i = _comparer.Compare(key, node.Key);
                if (i == 0) return node;
                node = i > 0 ? node.Right : node.Left;
            }
            return null;
        }

        /// <summary>
        /// Returns lower or equal key
        /// </summary>
        public TKey Lower(TKey key, TKey notFound = default(TKey))
        {
            var node = _root;
            Node good = null;
            while (node != null)
            {
                var i = _comparer.Compare(key, node.Key);
                if (i == 0) return key;
                if (i > 0)
                {
                    good = node;
                    node = node.Right;
                }
                else
                    node = node.Left;
            }
            return good == null ? notFound : good.Key;
        }

        /// <summary>
        /// Returns higher or equal key
        /// </summary>
        public TKey Higher(TKey key, TKey notFound = default(TKey))
        {
            var node = _root;
            Node good = null;
            while (node != null)
            {
                var i = _comparer.Compare(key, node.Key);
                if (i == 0) return key;
                if (i < 0)
                {
                    good = node;
                    node = node.Left;
                }
                else
                    node = node.Right;
            }
            return good == null ? notFound : good.Key;
        }

        /// <summary>
        /// Returns minimum region, which encloses given key
        /// </summary>
        public Tuple<TKey, TKey> Bounds(TKey key, TKey notFound = default(TKey))
        {
            var node = _root;
            Node lower = null;
            Node higher = null;
            while (node != null)
            {
                var i = _comparer.Compare(key, node.Key);
                if (i == 0) return new Tuple<TKey, TKey>(key, key);
                if (i < 0)
                {
                    higher = node;
                    node = node.Left;
                }
                else
                {
                    lower = node;
                    node = node.Right;
                }
            }
            return new Tuple<TKey, TKey>(lower == null ? notFound : lower.Key, higher == null ? notFound : higher.Key);
        }

        public void Add(TKey key, TValue value)
        {
            var node = new Node { Key = key, Value = value, Red = true };
            _items.Add(node);
            if (_root == null)
            {
                _root = node;
                _root.Red = false;
                return;
            }
            var x = _root;
            Node y = null;
            while (x != null)
            {
                y = x;
                x = _comparer.Compare(key, x.Key) > 0 ? x.Right : x.Left;
            }
            node.Parent = y;
            if (y == null) _root = node;
            else if (_comparer.Compare(key, y.Key) > 0) y.Right = node;
            else y.Left = node;
            InsertFixup(node);
        }

        private void InsertFixup(Node node)
        {
            while (true)
            {
                var parent = node.Parent;
                if (parent == null || !parent.Red || parent.Parent == null) break;
                var grand = parent.Parent;

                if (parent == grand.Left)
                {
                    var uncle = grand.Right;
                    if (uncle != null && uncle.Red) /* parent and uncle both red: we could swap colors of them with grand */
                    {
                        parent.Red = false;
                        uncle.Red = false;
                        grand.Red = true;
                        node = grand; /* and continue fixing tree for grand */
                    }
                    else if (node == parent.Right) /* we rotate around the parent */
                    {
                        node = parent;
                        RotateLeft(node);
                    }
                    else /* rotate around grand and switch colors of grand and parent */
                    {
                        parent.Red = false;
                        grand.Red = true;
                        RotateRight(grand);
                        if (grand == _root) _root = parent;
                        break; /* and finish since tree is fixed */
                    }
                }
                else
                {
                    if (_root.Parent != null) Debugger.Break();

                    var uncle = grand.Left;
                    if (uncle != null && uncle.Red) /* parent and uncle both red: we could swap colors of them with grand */
                    {
                        parent.Red = false;
                        uncle.Red = false;
                        grand.Red = true;
                        node = grand; /* and continue fixing tree for grand */
                    }
                    else if (node == parent.Left) /* we rotate around the parent */
                    {
                        node = parent;
                        RotateRight(node);
                    }
                    else /* rotate around grand and switch colors of grand and parent */
                    {
                        parent.Red = false;
                        grand.Red = true;
                        RotateLeft(grand);
                        if (grand == _root) _root = parent;
                        break; /* and finish since tree is fixed */
                    }
                }
            }
            _root.Red = false;
        }

        private void RotateLeft(Node node)
        {
            if (node.Right == null)
                throw new NotSupportedException("Cannot rotate left: right node is missing");
            var parent = node.Parent;
            var right = node.Right;
            right.Parent = parent;
            if (parent != null)
            {
                if (parent.Left == node) parent.Left = right;
                else parent.Right = right;
            }
            node.Right = right.Left;
            if (node.Right != null)
                node.Right.Parent = node;
            right.Left = node;
            node.Parent = right;
        }

        private void RotateRight(Node node)
        {
            if (node.Left == null)
                throw new NotSupportedException("Cannot rotate right: left node is missing");
            var parent = node.Parent;
            var left = node.Left;
            left.Parent = parent;
            if (parent != null)
            {
                if (parent.Left == node) parent.Left = left;
                else parent.Right = left;
            }
            node.Left = left.Right;
            if (node.Left != null)
                node.Left.Parent = node;
            left.Right = node;
            node.Parent = left;
        }

        public void Remove(TKey key)
        {
            var node = _search(key);
            if (node == null) return;

            var deleted = node.Left == null || node.Right == null ? node : Next(node);
            var child = deleted.Left ?? deleted.Right;
            if (child != null)
                child.Parent = deleted.Parent;
            if (deleted.Parent == null) /* root */
                _root = child;
            else if (deleted == deleted.Parent.Left)
                deleted.Parent.Left = child;
            else
                deleted.Parent.Right = child;
            if (node != deleted)
            {
                node.Key = deleted.Key;
                node.Value = deleted.Value;
            }

            if (!deleted.Red)
            {
                DeleteFixup(child, deleted.Parent);
            }
        }

        private void DeleteFixup(Node node, Node parent)
        {
            while (parent != null && (node == null || !node.Red))
            {
                if (node == parent.Left)
                {
                    var brother = parent.Right;
                    if (brother.Red)
                    {
                        brother.Red = false;
                        parent.Red = true;
                        RotateLeft(parent);
                        brother = parent.Right;
                    }
                    if ((brother.Left == null || !brother.Left.Red) && (brother.Right == null || !brother.Right.Red))
                    {
                        node = parent.Red ? _root : parent;
                        brother.Red = true;
                        parent.Red = false;
                    }
                    else
                    {
                        if (brother.Right == null || !brother.Right.Red)
                        {
                            if (brother.Left != null)
                                brother.Left.Red = false;
                            brother.Red = true;
                            RotateRight(brother);
                            brother = parent.Right;
                        }
                        if (!brother.Right.Red) Debugger.Break();
                        brother.Red = parent.Red;
                        parent.Red = false;
                        if (brother.Right != null)
                            brother.Right.Red = false;
                        RotateLeft(parent);
                        node = _root;
                    }
                }
                else
                {
                    var brother = parent.Left;
                    if (brother.Red)
                    {
                        brother.Red = false;
                        parent.Red = true;
                        RotateRight(parent);
                        brother = parent.Left;
                    }
                    if ((brother.Right == null || !brother.Right.Red) && (brother.Left == null || !brother.Left.Red))
                    {
                        node = parent.Red ? _root : parent;
                        brother.Red = true;
                        parent.Red = false;
                    }
                    else
                    {
                        if (brother.Left == null || !brother.Left.Red)
                        {
                            if (brother.Right != null)
                                brother.Right.Red = false;
                            brother.Red = true;
                            RotateLeft(brother);
                            brother = parent.Left;
                        }
                        brother.Red = parent.Red;
                        parent.Red = false;
                        if (brother.Left != null)
                            brother.Left.Red = false;
                        RotateRight(parent);
                        node = _root;
                    }
                }
                parent = node.Parent;
            }

            node.Red = false;
        }

        public TValue this[TKey key]
        {
            get
            {
                var node = _search(key);
                return node == null ? default(TValue) : node.Value;
            }
            set
            {
                var node = _search(key);
                if (node == null) Add(key, value);
                else node.Value = value;
            }
        }

        public bool Check()
        {
            int count;
            return _root == null || _root.Check(out count);
        }

        private Node Minimum(Node node)
        {
            var result = node;
            while (result.Left != null) result = result.Left;
            return result;
        }

        private Node Maximum(Node node)
        {
            var result = node;
            while (result.Right != null) result = result.Right;
            return result;
        }

        private Node Next(Node node)
        {
            Node result;
            if (node.Right != null)
            {
                result = Minimum(node.Right);
            }
            else
            {
                while (node.Parent != null && node.Parent.Right == node)
                    node = node.Parent;
                result = node.Parent;
            }
            return result;
        }

        private Node Previous(Node node)
        {
            Node result;
            if (node.Left != null)
            {
                result = Maximum(node.Left);
            }
            else
            {
                while (node.Parent != null && node.Parent.Left == node)
                    node = node.Parent;
                result = node.Parent;
            }
            return result;
        }

        public void Print()
        {
            var node = Minimum(_root);
            while (node != null)
            {
                Console.Write(node.Red ? '*' : ' ');
                Console.Write(node.Key);
                Console.Write('\t');
                node = Next(node);
            }
        }

        #region Nested classes

        [DebuggerDisplay("{ToString()}")]
        private sealed class Node
        {
            public TKey Key;
            public TValue Value;
            public bool Red;

            public Node Parent;
            public Node Left;
            public Node Right;

            public bool Check(out int count)
            {
                count = 0;
                if (Red)
                {
                    if (Left != null && Left.Red) return false;
                    if (Right != null && Right.Red) return false;
                }
                var left = 0;
                var right = 0;
                if (Left != null && !Left.Check(out left)) return false;
                if (Right != null && !Right.Check(out right)) return false;
                count = left + (Red ? 0 : 1);
                if (left != right)
                {
                    ;
                }
                return left == right;
            }

            public override string ToString()
            {
                return ToShortString(2);
            }

            private string ToShortString(int depth = 0)
            {
                return depth == 0
                    ? string.Format("{0}{1}", Red ? "*" : "", Key)
                    : string.Format("({1}<{0}>{2})", ToShortString(), Left == null ? "null" : Left.ToShortString(depth - 1), Right == null ? "null" : Right.ToShortString(depth - 1));
            }
        }

        #endregion

        #region IEnumerable

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            throw new NotImplementedException();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion
    }

    public class RedBlackTreeSimple<TKey>
    {
        private readonly List<Node> _items;
        private Node _root;
        private readonly IComparer<TKey> _comparer;

        public RedBlackTreeSimple(int capacity = 10)
        {
            _items = new List<Node>(capacity);
            _comparer = Comparer<TKey>.Default;
        }

        private Node _search(TKey key)
        {
            var node = _root;
            while (node != null)
            {
                var i = _comparer.Compare(key, node.Key);
                if (i == 0) return node;
                node = i > 0 ? node.Right : node.Left;
            }
            return null;
        }

        /// <summary>
        /// Returns lower or equal key
        /// </summary>
        public TKey Lower(TKey key, TKey notFound = default(TKey))
        {
            var node = _root;
            Node good = null;
            while (node != null)
            {
                var i = _comparer.Compare(key, node.Key);
                if (i == 0) return key;
                if (i > 0)
                {
                    good = node;
                    node = node.Right;
                }
                else
                    node = node.Left;
            }
            return good == null ? notFound : good.Key;
        }

        /// <summary>
        /// Returns higher or equal key
        /// </summary>
        public TKey Higher(TKey key, TKey notFound = default(TKey))
        {
            var node = _root;
            Node good = null;
            while (node != null)
            {
                var i = _comparer.Compare(key, node.Key);
                if (i == 0) return key;
                if (i < 0)
                {
                    good = node;
                    node = node.Left;
                }
                else
                    node = node.Right;
            }
            return good == null ? notFound : good.Key;
        }

        /// <summary>
        /// Returns minimum region, which encloses given key
        /// </summary>
        public Tuple<TKey, TKey> Bounds(TKey key, TKey notFound = default(TKey))
        {
            var node = _root;
            Node lower = null;
            Node higher = null;
            while (node != null)
            {
                var i = _comparer.Compare(key, node.Key);
                if (i == 0) return new Tuple<TKey, TKey>(key, key);
                if (i < 0)
                {
                    higher = node;
                    node = node.Left;
                }
                else
                {
                    lower = node;
                    node = node.Right;
                }
            }
            return new Tuple<TKey, TKey>(lower == null ? notFound : lower.Key, higher == null ? notFound : higher.Key);
        }

        public void Add(TKey key)
        {
            var node = new Node(key);
            _items.Add(node);
            if (_root == null)
            {
                _root = node;
                _root.Red = false;
                return;
            }
            var x = _root;
            Node y = null;
            while (x != null)
            {
                y = x;
                x = _comparer.Compare(key, x.Key) > 0 ? x.Right : x.Left;
            }
            node.Parent = y;
            if (y == null) _root = node;
            else if (_comparer.Compare(key, y.Key) > 0) y.Right = node;
            else y.Left = node;
            InsertFixup(node);
        }

        private void InsertFixup(Node node)
        {
            while (true)
            {
                var parent = node.Parent;
                if (parent == null || !parent.Red || parent.Parent == null) break;
                var grand = parent.Parent;

                if (parent == grand.Left)
                {
                    var uncle = grand.Right;
                    if (uncle != null && uncle.Red) /* parent and uncle both red: we could swap colors of them with grand */
                    {
                        parent.Red = false;
                        uncle.Red = false;
                        grand.Red = true;
                        node = grand; /* and continue fixing tree for grand */
                    }
                    else if (node == parent.Right) /* we rotate around the parent */
                    {
                        node = parent;
                        RotateLeft(node);
                    }
                    else /* rotate around grand and switch colors of grand and parent */
                    {
                        parent.Red = false;
                        grand.Red = true;
                        RotateRight(grand);
                        if (grand == _root) _root = parent;
                        break; /* and finish since tree is fixed */
                    }
                }
                else
                {
                    if (_root.Parent != null) Debugger.Break();

                    var uncle = grand.Left;
                    if (uncle != null && uncle.Red) /* parent and uncle both red: we could swap colors of them with grand */
                    {
                        parent.Red = false;
                        uncle.Red = false;
                        grand.Red = true;
                        node = grand; /* and continue fixing tree for grand */
                    }
                    else if (node == parent.Left) /* we rotate around the parent */
                    {
                        node = parent;
                        RotateRight(node);
                    }
                    else /* rotate around grand and switch colors of grand and parent */
                    {
                        parent.Red = false;
                        grand.Red = true;
                        RotateLeft(grand);
                        if (grand == _root) _root = parent;
                        break; /* and finish since tree is fixed */
                    }
                }
            }
            _root.Red = false;
        }

        private void RotateLeft(Node node)
        {
            if (node.Right == null)
                throw new NotSupportedException("Cannot rotate left: right node is missing");
            var parent = node.Parent;
            var right = node.Right;
            right.Parent = parent;
            if (parent != null)
            {
                if (parent.Left == node) parent.Left = right;
                else parent.Right = right;
            }
            node.Right = right.Left;
            if (node.Right != null)
                node.Right.Parent = node;
            right.Left = node;
            node.Parent = right;
        }

        private void RotateRight(Node node)
        {
            if (node.Left == null)
                throw new NotSupportedException("Cannot rotate right: left node is missing");
            var parent = node.Parent;
            var left = node.Left;
            left.Parent = parent;
            if (parent != null)
            {
                if (parent.Left == node) parent.Left = left;
                else parent.Right = left;
            }
            node.Left = left.Right;
            if (node.Left != null)
                node.Left.Parent = node;
            left.Right = node;
            node.Parent = left;
        }

        public void Remove(TKey key)
        {
            var node = _search(key);
            if (node == null) return;

            var deleted = node.Left == null || node.Right == null ? node : Next(node);
            var child = deleted.Left ?? deleted.Right;
            if (child != null)
                child.Parent = deleted.Parent;
            if (deleted.Parent == null) /* root */
                _root = child;
            else if (deleted == deleted.Parent.Left)
                deleted.Parent.Left = child;
            else
                deleted.Parent.Right = child;
            if (node != deleted)
            {
                node.Key = deleted.Key;
            }

            if (!deleted.Red)
            {
                DeleteFixup(child, deleted.Parent);
            }
        }

        private void DeleteFixup(Node node, Node parent)
        {
            while (parent != null && (node == null || !node.Red))
            {
                if (node == parent.Left)
                {
                    var brother = parent.Right;
                    if (brother.Red)
                    {
                        brother.Red = false;
                        parent.Red = true;
                        RotateLeft(parent);
                        brother = parent.Right;
                    }
                    if ((brother.Left == null || !brother.Left.Red) && (brother.Right == null || !brother.Right.Red))
                    {
                        node = parent.Red ? _root : parent;
                        brother.Red = true;
                        parent.Red = false;
                    }
                    else
                    {
                        if (brother.Right == null || !brother.Right.Red)
                        {
                            if (brother.Left != null)
                                brother.Left.Red = false;
                            brother.Red = true;
                            RotateRight(brother);
                            brother = parent.Right;
                        }
                        if (!brother.Right.Red) Debugger.Break();
                        brother.Red = parent.Red;
                        parent.Red = false;
                        if (brother.Right != null)
                            brother.Right.Red = false;
                        RotateLeft(parent);
                        node = _root;
                    }
                }
                else
                {
                    var brother = parent.Left;
                    if (brother.Red)
                    {
                        brother.Red = false;
                        parent.Red = true;
                        RotateRight(parent);
                        brother = parent.Left;
                    }
                    if ((brother.Right == null || !brother.Right.Red) && (brother.Left == null || !brother.Left.Red))
                    {
                        node = parent.Red ? _root : parent;
                        brother.Red = true;
                        parent.Red = false;
                    }
                    else
                    {
                        if (brother.Left == null || !brother.Left.Red)
                        {
                            if (brother.Right != null)
                                brother.Right.Red = false;
                            brother.Red = true;
                            RotateLeft(brother);
                            brother = parent.Left;
                        }
                        brother.Red = parent.Red;
                        parent.Red = false;
                        if (brother.Left != null)
                            brother.Left.Red = false;
                        RotateRight(parent);
                        node = _root;
                    }
                }
                parent = node.Parent;
            }

            node.Red = false;
        }

        private Node Minimum(Node node)
        {
            var result = node;
            while (result.Left != null) result = result.Left;
            return result;
        }

        private Node Maximum(Node node)
        {
            var result = node;
            while (result.Right != null) result = result.Right;
            return result;
        }

        private Node Next(Node node)
        {
            Node result;
            if (node.Right != null)
            {
                result = Minimum(node.Right);
            }
            else
            {
                while (node.Parent != null && node.Parent.Right == node)
                    node = node.Parent;
                result = node.Parent;
            }
            return result;
        }

        private Node Previous(Node node)
        {
            Node result;
            if (node.Left != null)
            {
                result = Maximum(node.Left);
            }
            else
            {
                while (node.Parent != null && node.Parent.Left == node)
                    node = node.Parent;
                result = node.Parent;
            }
            return result;
        }

        public void Print()
        {
            var node = Minimum(_root);
            while (node != null)
            {
                Console.Write(node.Red ? '*' : ' ');
                Console.Write(node.Key);
                Console.Write('\t');
                node = Next(node);
            }
        }

        #region Nested classes

        private sealed class Node
        {
            public Node(TKey key)
            {
                Key = key;
            }

            public TKey Key;
            public bool Red = true;

            public Node Parent;
            public Node Left;
            public Node Right;
        }

        #endregion
    }

    /// <summary>
    /// Fenwick tree for summary on the array
    /// </summary>
    public class FenwickSum
    {
        public readonly int Size;
        private readonly int[] _items;
        public FenwickSum(int size)
        {
            Size = size;
            _items = new int[Size];
        }

        public FenwickSum(IList<int> items)
            : this(items.Count)
        {
            for (var i = 0; i < Size; i++)
                Increase(i, items[i]);
        }

        private int Sum(int r)
        {
            if (r < 0) return 0;
            if (r >= Size) r = Size - 1;
            var result = 0;
            for (; r >= 0; r = (r & (r + 1)) - 1)
                result += _items[r];
            return result;
        }

        private int Sum(int l, int r)
        {
            return Sum(r) - Sum(l - 1);
        }

        public int this[int r]
        {
            get { return Sum(r); }
        }

        public int this[int l, int r]
        {
            get { return Sum(l, r); }
        }

        public void Increase(int i, int delta)
        {
            for (; i < Size; i = i | (i + 1))
                _items[i] += delta;
        }
    }

    /// <summary>
    /// Prime numbers
    /// </summary>
    public class Primes
    {
        /// <summary>
        /// Returns prime numbers in O(n)
        /// Returns lowet divisors as well
        /// Memory O(n)
        /// </summary>
        public static void ImprovedSieveOfEratosthenes(int n, out int[] lp, out List<int> pr)
        {
            lp = new int[n];
            pr = new List<int>();
            for (var i = 2; i < n; i++)
            {
                if (lp[i] == 0)
                {
                    lp[i] = i;
                    pr.Add(i);
                }
                foreach (var prJ in pr)
                {
                    var prIj = i * prJ;
                    if (prJ <= lp[i] && prIj <= n - 1)
                    {
                        lp[prIj] = prJ;
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// Returns prime numbers in O(n*n)
        /// </summary>
        public static void SieveOfEratosthenes(int n, out List<int> pr)
        {
            var m = 50000;//(int)(3l*n/(long)Math.Log(n)/2);
            pr = new List<int>();
            var f = new bool[m];
            for (var i = 2; i * i <= n; i++)
                if (!f[i])
                    for (var j = (long)i * i; j < m && j * j < n; j += i)
                        f[j] = true;
            pr.Add(2);
            for (var i = 3; i * i <= n; i += 2)
                if (!f[i])
                    pr.Add(i);
        }

        /// <summary>
        /// Greatest common divisor
        /// </summary>
        public static int Gcd(int x, int y)
        {
            while (y != 0)
            {
                var c = y;
                y = x % y;
                x = c;
            }
            return x;
        }

        /// <summary>
        /// Greatest common divisor
        /// </summary>
        public static long Gcd(long x, long y)
        {
            while (y != 0)
            {
                var c = y;
                y = x % y;
                x = c;
            }
            return x;
        }

        /// <summary>
        /// Greatest common divisor
        /// </summary>
        public static BigInteger Gcd(BigInteger x, BigInteger y)
        {
            while (y != 0)
            {
                var c = y;
                y = x % y;
                x = c;
            }
            return x;
        }

        /// <summary>
        /// Returns all divisors of n in O(√n)
        /// </summary>
        public static IEnumerable<long> GetDivisors(long n)
        {
            long r;
            while (true)
            {
                var x = Math.DivRem(n, 2, out r);
                if (r != 0) break;
                n = x;
                yield return 2;
            }
            var i = 3;
            while (i <= Math.Sqrt(n))
            {
                var x = Math.DivRem(n, i, out r);
                if (r == 0)
                {
                    n = x;
                    yield return i;
                }
                else i += 2;
            }
            if (n != 1) yield return n;
        }
    }

    /// <summary>
    /// Graph matching algorithms
    /// </summary>
    public class GraphMatching
    {
        #region Kuhn algorithm

        /// <summary>
        /// Kuhn algorithm for finding maximal matching
        /// in bipartite graph
        /// Vertexes are numerated from zero: 0, 1, 2, ... n-1
        /// Return array of matchings
        /// </summary>
        public static int[] Kuhn(List<int>[] g, int leftSize, int rightSize)
        {
            Debug.Assert(g.Count() == leftSize);

            var matching = new int[leftSize];
            for (var i = 0; i < rightSize; i++)
                matching[i] = -1;
            var used = new bool[leftSize];

            for (var v = 0; v < leftSize; v++)
            {
                Array.Clear(used, 0, leftSize);
                _kuhn_dfs(v, g, matching, used);
            }

            return matching;
        }

        private static bool _kuhn_dfs(int v, List<int>[] g, int[] matching, bool[] used)
        {
            if (used[v]) return false;
            used[v] = true;
            for (var i = 0; i < g[v].Count; i++)
            {
                var to = g[v][i];
                if (matching[to] == -1 || _kuhn_dfs(matching[to], g, matching, used))
                {
                    matching[to] = v;
                    return true;
                }
            }
            return false;
        }

        #endregion
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Codeforces
{
    class Program
    {
        static void Main(string[] args)
        {
            int count = Convert.ToInt32(Console.ReadLine().Split()[1]);
            string[] a = Console.ReadLine().Split();
            int[] prices = new int[a.Length];
            for (int i = 0; i < a.Length; i++)
                prices[i] = Convert.ToInt32(a[i]);

            Dictionary<string, int> fruits = new Dictionary<string, int>();
            int loop = count;
            count = 0;
            for (int i = 0; i < loop; i++)
            {
                string now = Console.ReadLine();
                try
                {
                    fruits[now]++;
                }
                catch
                {
                    fruits.Add(now,1);
                    count++;
                }
            }

            int[] all = new int[count];
            int index = 0;
            foreach (int i in fruits.Values)
            {
                all[index] = i;
                index++;
            }

            Array.Sort(all);
            Array.Sort(prices);

            int min = 0;
            for (int i = 0; i < all.Length; i++)
                min += all[all.Length - 1 - i] * prices[i];

            int max = 0;
            for (int i = 0; i < all.Length; i++)
                max += all[all.Length - 1 - i] * prices[prices.Length - 1 - i];

            Console.Write("{0} {1}", min, max);
        }
    }
}


using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] n = Console.ReadLine().Split(' ');
            string[] prices = Console.ReadLine().Split(' ');
            string[] fruits = new string[int.Parse(n[1])];
            int[] pr = new int[int.Parse(n[0])];
            int[] pr2 = new int[int.Parse(n[0])];
            int[] kol = new int[fruits.Length];
            for (int i = 0; i < int.Parse(n[1]); i++)
            {
                fruits[i] = Console.ReadLine();
            }
            for (int i = 0; i < int.Parse(n[0]); i++)
            {
                pr2[i] = int.Parse(prices[i]);
            }

            for (int i = 0; i < int.Parse(n[0]); i++)
            {
                pr[i] = int.Parse(prices[i]);
            }

            int count = 0;
            for (int i = 0; i < fruits.Length; i++)
            {
                if (fruits[i] == "0")
                    continue;
                count = 1;
                for (int j = i + 1; j < fruits.Length; j++)
                {
                    if (fruits[i] == fruits[j] && fruits[j] != "0" && fruits[i] != "0")
                    {
                        count++;
                        fruits[j] = "0";
                    }
                }
                fruits[i] = "0";
                kol[i] = count;
            }

            int summamin = 0;
            int min = 100;
            int k = 0;
            Array.Sort(kol);
            Array.Reverse(kol);

            for (int i = 0; i < kol.Length; i++)
            {
                min = 100;
                k = 0;
                for (int j = 0; j < pr.Length;j++)
                {
                    if (pr[j] < min && pr[j] != -1)
                    {
                        min = pr[j];
                        k = j;
                    }
                }
                pr[k] = -1;
                summamin += kol[i] * min;
            }

            int summamax = 0;
            int max = 0;
            for (int i = 0; i < kol.Length; i++)
            {
                k = 0;
                max = 0;
                for (int j = 0; j < pr2.Length; j++)
                {
                    if (pr2[j] > max)
                    {
                        max = pr2[j];
                        k = j;
                    }
                }
                pr2[k] = -1;
                summamax += kol[i] * max;
            }
            Console.WriteLine(summamin + " " + summamax);
            Console.ReadLine();
        }
    }
}


using System;
using System.Collections.Generic;
using System.Linq;

/*Он составил список из m фруктов, которые хотел бы приобрести. В том случае, если Валера хотел купить несколько фруктов одного сорта,
 * то он несколько раз заносил этот фрукт в свой список.
Подойдя к фруктовому ларьку Ашота, мальчик заметил, что продавец еще не успел наклеить ценники на свой товар, а только лишь разложил их на прилавке.
 * В скором времени, когда Ашот присвоит каждому фрукту его цену, Валера сможет подсчитать, сколько денег ему потребуется для приобретения всего
 * желаемого из списка. Однако мальчик хочет заранее знать минимальную (в случае «самого удачного» для Валеры распределения ценников) и максимальную
 * (в случае «самого неудачного» для Валеры распределения ценников) стоимости покупки.
Входные данные
В первой строке входных данных записаны два целых числа n и m (1 ≤ n, m ≤ 100) — количество ценников, а, следовательно, и различных сортов фруктов,
 * имеющихся у продавца Ашота, и количество записей в списке Валерия. Во второй строке через пробелы записаны n целых положительных чисел.
 * Каждое из этих чисел не превосходит 100 и означает цену одного фрукта определенного сорта. В последующих m строках записаны названия фруктов,
 * которые желает приобрести мальчик. Каждое название состоит из строчных букв латинского алфавита и имеет длину от 1 до 32 символов.
 * Гарантируется, что число различных фруктов в списке не превосходит n. Также гарантируется, что у продавца в наличии имеются все фрукты,
 * которые желает приобрести Валера.
Выходные данные
Выведите через пробел два числа a и b (a ≤ b) — минимальную и максимальную суммы, необходимые Валере для того, чтобы приобрести все фрукты из своего списка.

 */
namespace Fructs
{
    class Program
    {
        static void Main()
        {
            string[] nums = Console.ReadLine().Split(' ');
            int n = int.Parse(nums[0]);
            int m = int.Parse(nums[1]);
            nums = Console.ReadLine().Split(' ');
            int[] prices = new int[n];
            for (int i = 0; i < n; i++)
                prices[i] = int.Parse(nums[i]);
            Dictionary< string, int> fructs = new Dictionary<string, int>();
            for (int i = 0; i < m; i++)
            {
                string name = Console.ReadLine();
                if (fructs.ContainsKey(name))
                {
                    fructs[name]++;
                }
                else fructs.Add(name, 1);
            }
            Array .Sort(prices);
            var res = from t in fructs orderby t.Value descending select t;
            int j = 0;
            int minSum = 0;
            int maxSum = 0;
            foreach (var pair in res)
            {
                minSum += pair.Value*prices[j];
                maxSum += pair.Value*prices[n - 1 - j];
                j++;
            }
            Console.WriteLine("{0} {1}", minSum, maxSum);
        }
    }
}


using System;
using System.Collections.Generic;
class X{
	public static void Main(String[] args){
		System.Console.ReadLine();
		List<Int32> prices = (new List<String>(System.Console.ReadLine().Split(" ".ToCharArray()))).ConvertAll(delegate(String s){ return Int32.Parse(s); });

		List<String> items = new List<String>();

		while(true){
				String s = System.Console.ReadLine();
				if (s == null){
						break;
				}
				items.Add(s);

		}

		List<Int32> counts = new List<Int32>();
		while(items.Count > 0){
				string s1 = items[0];
				int x = items.RemoveAll(delegate(String s){ return s == s1; });
				counts.Add(x);
		}

		counts.Sort();
		counts.Reverse();

		prices.Sort();

		int low = 0;

		for(int i = 0; i < counts.Count;i++){
				low = low + counts[i] * prices[i];
		}

		int high = 0;
		prices.Reverse();

		for(int i = 0; i < counts.Count;i++){
				high = high + counts[i] * prices[i];
		}

		System.Console.WriteLine("{0:d} {1:d}",low,high);
	}
}


using System;
using System.Collections.Generic;
class X{
	public static void Main(String[] args){
		System.Console.ReadLine();
		List<Int32> prices = (new List<String>(System.Console.ReadLine().Split(" ".ToCharArray()))).ConvertAll(delegate(String s){ return Int32.Parse(s); });

		List<String> items = new List<String>();

		while(true){
			String s = System.Console.ReadLine();
			if (s == null){
				break;
			}
			items.Add(s);

		}

		List<Int32> counts = new List<Int32>();
		while(items.Count > 0){
			int x = items.RemoveAll(delegate(String s){ return s == items[0]; });
			counts.Add(x);
		}

		counts.Sort();
		counts.Reverse();

		prices.Sort();

		int low = 0;

		for(int i = 0; i < counts.Count;i++){
			low = low + counts[i] * prices[i];
		}

		int high = 0;
		prices.Reverse();

		for(int i = 0; i < counts.Count;i++){
			high = high + counts[i] * prices[i];
		}

		System.Console.WriteLine("{0:d} {1:d}",low,high);
	}
}


using System;
using System.Collections.Generic;
class X{
		public static void Main(String[] args){
				System.Console.ReadLine();
				List<Int32> prices = (new List<String>(System.Console.ReadLine().Split(" ".ToCharArray()))).ConvertAll(delegate(String s){ return Int32.Parse(s); });

				List<String> items = new List<String>();

				while(true){
						String s = System.Console.ReadLine();
						if (s == null){
								break;
						}
						items.Add(s);

				}

				List<Int32> counts = new List<Int32>();
				while(items.Count > 0){
						string s1 = items[0];
						int x = items.RemoveAll(delegate(String s){ return s == s1; });
						counts.Add(x);
				}

				counts.Sort();
				counts.Reverse();

				prices.Sort();

				int low = 0;

				for(int i = 0; i < counts.Count;i++){
						low = low + counts[i] * prices[i];
				}

				int high = 0;
				prices.Reverse();

				for(int i = 0; i < counts.Count;i++){
						high = high + counts[i] * prices[i];
				}

				System.Console.WriteLine("{0:d} {1:d}",low,high);
		}
}


using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;

class Program
{
  class Pair : System.IComparable
  {
    public int x;
    public int y;

    public Pair (int xx, int yy)
    {
      x = xx;
      y = yy;
    }

    public int CompareTo(object obj)
    {
      Pair p = (Pair) obj;
      return x < p.x ? -1 : x == p.x ? 0 : 1;
    }
  }

  static void Main(string[] args)
  {
    //Console.SetIn(File.OpenText("../../input.txt"));

    var dict = new Dictionary<string, int>();
    string[] ss = Console.ReadLine().Split(' ');
    int n = int.Parse(ss[0]);
    int m = int.Parse(ss[1]);

    ss = Console.ReadLine().Split(' ');
    int[] a = new int[n];
    for (int i = 0; i < ss.Length; ++i)
      a[i] = int.Parse(ss[i]);

    for (int i = 0; i < m; ++i)
    {
      string s = Console.ReadLine();
      if (dict.ContainsKey(s))
        dict[s]++;
      else
        dict.Add(s, 1);
    }

    int[] d = new int[dict.Count];
    int j = 0;
    foreach (var i in dict.Values)
      d[j++] = i;

    Array.Sort(a);
    Array.Sort(d);

    int min = 0;
    int max = 0;

    for (int i = 0; i < d.Length; ++i)
    {
      max += a[a.Length - i - 1] * d[d.Length - i - 1];
      min += a[i] * d[d.Length - i - 1];
    }

    Console.WriteLine(min + " " + max);

    /*
    string str = "";
    while (!string.IsNullOrEmpty((str = Console.ReadLine())))

    var dict = new System.Collections.Generic.Dictionary<string, int>();

    Pair[] a = new Pair[100];
    for (int i = 0; i < a.Length; ++i)
      a[i] = new Pair(100 - i, i);
    Array.Sort(a);

    int testCount = Convert.ToInt32(Console.ReadLine());
    int n = int.Parse(Console.ReadLine());
    string[] data = Console.ReadLine().Split(new char[] { ' ' });
    */

    Console.ReadKey();
  }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Contest_12
{
    class Program
    {
        static void B()
        {
            var s = Console.ReadLine();
            string r1 = Console.ReadLine();
            string r2 = "";
            int[] a = new int[10];

            for (int i = 0; i < s.Length; i++)
            {
                a[int.Parse(s[i].ToString())]++;
            }

            for (int i = 1; i < 10; i++)
            {
                if (a[i] > 0)
                {
                    r2 += i;
                    a[i]--;
                    break;
                }
            }

            for (int i = 0; i < 10; i++)
            {
                while (a[i] > 0)
                {
                    r2 += i;
                    a[i]--;
                }
            }

            Console.WriteLine(r1 == r2 ? "OK" : "WRONG_ANSWER");
        }

        static void C()
        {
            var t = Console.ReadLine().Split(' '); ;
            int n = int.Parse(t[0]);
            int m = int.Parse(t[1]);

            t = Console.ReadLine().Split(' ');
            SortedDictionary<int, int> a = new SortedDictionary<int, int>();
            SortedDictionary<int, int> b = new SortedDictionary<int, int>();

            foreach (var e in t)
            {
                int x = int.Parse(e);
                try
                {
                    a.Add(x, 1);
                    b.Add(x, 1);
                }
                catch
                {
                    a[x]++;
                    b[x]++;
                }
            }

            Dictionary<string, int> f = new Dictionary<string, int>();
            for (int i = 0; i < m; i++)
            {
                string s = Console.ReadLine();
                try
                {
                    f.Add(s, 1);
                }
                catch
                {
                    f[s]++;
                }
            }

            var zn = f.OrderByDescending(a1 => a1.Value);

            var k = a.Keys.ToArray();
            var k2 = k.Reverse().ToArray();
            int j = 0;

            int min = 0;
            int max = 0;
            foreach (var e in zn)
            {
                if (a[k[j]] == 0)
                    j++;
                    min += k[j] * e.Value;
                    a[k[j]]--;
            }

            j = 0;

            foreach (var e in zn)
            {
                if (b[k2[j]] == 0)
                    j++;
                max += k2[j] * e.Value;
                b[k2[j]]--;
            }

            Console.WriteLine(min + " " + max);
        }

        static void Main(string[] args)
        {
            C();
        }
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] names = Console.ReadLine().Split(' ');
            int[] count = { int.Parse(names[0]), int.Parse(names[1]) };
            int[] much = new int[count[0]];
            int i=0;
            foreach (string s in Console.ReadLine().Split(' '))
                much[i++] = int.Parse(s);
            names = new string[count[1]];
            for (i = 0; i < count[1]; ++i)
                names[i] = Console.ReadLine();
            Dictionary<string, int> dic = new Dictionary<string, int>();
            foreach (string s in names)
            {
                if (dic.ContainsKey(s)) ++dic[s];
                else dic.Add(s, 1);
            }

            List<KeyValuePair<string, int>> tmp = new List<KeyValuePair<string, int>>(dic.Count);
            foreach (KeyValuePair<string, int> item in dic)
                tmp.Add(item);
            tmp.Sort((x, y) => { return x.Value.CompareTo(y.Value); });
            tmp.Reverse();

            Array.Sort(much);

            int v=0;
            for (i = 0; i < tmp.Count; ++i)
                v += much[i] * tmp[i].Value;
            Console.Write(v.ToString() + " ");

            Array.Reverse(much);
            v = 0;
            for (i = 0; i < tmp.Count; ++i)
                v += much[i] * tmp[i].Value;
            Console.Write(v.ToString());
        }
    }
}

            /* string s=Console.ReadLine();
			char[] cs=s.ToCharArray();
            Array.Sort(cs);
            for(int i=0;i<cs.Length;++i)
                if(cs[i]!='0'){
                    char tmp=cs[0];
                    cs[0]=cs[i];
                    cs[i]=tmp;
                    break;
                }
            foreach (char c in cs)
                Console.Write(c);
               if (Console.Read() != c)
                {
                    Console.Write("WRONG_ANSWER");
                    return;
                }
            Console.Write("OK");*/


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace c
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] fruithc = new int[110];
            List<int> count = new List<int>();
            int length = 0;
            bool find;
            string[] token = Console.ReadLine().Split();
            int n = int.Parse(token[0]), k = int.Parse(token[1]);
            token = Console.ReadLine().Split();
            int[] cost = new int[n];
            for (int i = 0; i < n; i++)
            {
                cost[i] = int.Parse(token[i]);
            }
            Array.Sort(cost);
            int cur, index = 0;
            for (int i = 0; i < k; i++)
            {
                cur = Console.ReadLine().GetHashCode();
                find = false;
                for (int j = 0; j < length; j++)
                {
                    if (fruithc[j] == cur)
                    {
                        find = true;
                        index = j;
                        break;
                    }
                }
                if (find)
                {
                    count[index]++;
                }
                else
                {
                    count.Add(1);
                    fruithc[length] = cur;
                    length++;
                }
            }
            count.Sort();
            int min = 0, max = 0;
            index = n-1;
            for (int i = 0; i < count.Count; i++)
            {
                max += (cost[index] * count[count.Count - i - 1]);
                min += (cost[i] * count[count.Count - i - 1]);
                index--;
            }
            Console.WriteLine("{0} {1}", min, max);
        }
    }
}


use std::{cmp::Reverse, io::stdin};

fn get_input(input: &mut String) {
    input.clear();
    stdin().read_line(input).expect("error");
}

fn main() {
    let mut input = String::new();
    get_input(&mut input);
    let mut numbers = input.trim().split_whitespace();
    let _n = numbers.next().unwrap().parse::<u32>().unwrap();
    let m = numbers.next().unwrap().parse::<u32>().unwrap();

    get_input(&mut input);
    let mut prices: Vec<_> = input
        .trim()
        .split_whitespace()
        .filter_map(|s| s.parse::<u32>().ok())
        .collect();
    prices.sort();

    let mut fruits = Vec::<String>::new();
    for _ in 0..m {
        get_input(&mut input);
        fruits.push(input.trim().to_string());
    }
    fruits.sort_unstable();

    let mut counts = Vec::<u32>::new();
    let mut previous = &fruits[0];
    let mut sum = 1 as u32;

    for fruit in &fruits[1..] {
        if fruit == previous {
            sum += 1;
        } else {
            counts.push(sum);
            sum = 1;
            previous = fruit;
        }
    }
    counts.push(sum);
    counts.sort_by_key(|&b| Reverse(b));

    let min = counts
        .iter()
        .zip(prices.iter().take(counts.len()))
        .map(|(a, b)| a * b)
        .sum::<u32>();
    prices.reverse();
    let max = counts
        .iter()
        .zip(prices.iter().take(counts.len()))
        .map(|(a, b)| a * b)
        .sum::<u32>();

    println!("{} {}", min, max);
}


<?php
list($a, $b) = explode(" ", trim(fgets(STDIN)));
$c = explode(" ", trim(fgets(STDIN)));
sort($c);
$d = array();
$g = 0;
$h = array();
$i = array();
$l = array();
for($x = 1; $x <= $b; $x++)
{
    $e = trim(fgets(STDIN));
    array_push($d, $e);
}
$f = array_unique($d);
sort($f);
for($x = 0; $x < count($f); $x++)
{
    for($y = 0; $y < count($d); $y++)
    {
        if($f[$x] == $d[$y])
        {
            $g++;
        }
    }
    array_push($h, $g);
    $g = 0;
}
rsort($h);
for($x = 0; $x < count($h); $x++)
{
    $j = $h[$x] * $c[$x];
    array_push($i, $j);
}
$j = array_sum($i);
rsort($c);
for($x = 0; $x < count($h); $x++)
{
    $k = $h[$x] * $c[$x];
    array_push($l, $k);
}
$k = array_sum($l);
print $j . " " . $k;
?>


<?php

$in = 'php://stdin';
//$in = 'input.txt';

$data = file($in);

array_shift($data);

$prices = explode(' ', array_shift($data));

while (!$data[count($data) - 1]) {
	array_pop($data);
}

$mprices = $prices;

sort($prices, SORT_NUMERIC);
rsort($mprices, SORT_NUMERIC);

$fruits = array_count_values($data);
rsort($fruits, SORT_NUMERIC);

$a = $b = 0;
foreach ($fruits as $num) {
	$a += $num * array_shift($prices);
	$b += $num * array_shift($mprices);
}

echo "$a $b";


<?php

$in = 'php://stdin';
//$in = 'input.txt';

$data = file($in);

array_shift($data);

$prices = explode(' ', array_shift($data));

if (!$data[count($data) - 1]) {
	array_pop($data);
}

$mprices = $prices;

sort($prices);
rsort($mprices);

$fruits = array_count_values($data);
rsort($fruits);

$a = $b = 0;
foreach ($fruits as $num) {
	$a += $num * array_shift($prices);
	$b += $num * array_shift($mprices);
}

echo "$a $b";


<?php

$in = 'php://stdin';
//$in = 'input.txt';

$data = file($in);

array_shift($data);

$prices = explode(' ', array_shift($data));

while (!$data[count($data) - 1]) {
	array_pop($data);
}

$mprices = $prices;

sort($prices);
rsort($mprices);

$fruits = array_count_values($data);
rsort($fruits);

$a = $b = 0;
foreach ($fruits as $num) {
	$a += $num * array_shift($prices);
	$b += $num * array_shift($mprices);
}

echo "$a $b";


<?php
$nFruits = explode(' ', trim(fgets(STDIN)));
$nFruits = $nFruits[1];
$prices = explode(' ', trim(fgets(STDIN)));
$pricesDESC = $prices;

sort($prices, SORT_NUMERIC);
rsort($pricesDESC, SORT_NUMERIC);

for ($i=0;$i<$nFruits;$i++) {
    $fruits[] = trim(fgets(STDIN));
}

$fruits = array_count_values($fruits);
rsort($fruits, SORT_NUMERIC);

$a = $b = 0;
foreach ($fruits as $num) {
    $a += $num * array_shift($prices);
    $b += $num * array_shift($pricesDESC);
}

echo "$a $b";
?>


<?php
$nFruits = explode(' ', trim(fgets(STDIN)));
$nFruits = $nFruits[1];
$prices = explode(' ', trim(fgets(STDIN)));
$pricesDESC = $prices;

sort($prices, SORT_NUMERIC);
rsort($pricesDESC, SORT_NUMERIC);

for ($i=0;$i<$nFruits;$i++) {
    $fruits[] = trim(fgets(STDIN));
}

$fruits = array_count_values($fruits);
rsort($fruits, SORT_NUMERIC);

print_r($fruits);

$a = $b = 0;
foreach ($fruits as $num) {
    $a += $num * array_shift($prices);
    $b += $num * array_shift($pricesDESC);
}

echo "$a $b";
?>


<?php
trim(fgets(STDIN));
$prices = explode(' ', trim(fgets(STDIN)));
$pricesDESC = $prices;

sort($prices, SORT_NUMERIC);
rsort($pricesDESC, SORT_NUMERIC);

while ($fruits[] = trim(fgets(STDIN)))

$fruits = array_count_values($fruits);
rsort($fruits, SORT_NUMERIC);

$a = $b = 0;
foreach ($fruits as $num) {
    $a += $num * array_shift($prices);
    $b += $num * array_shift($pricesDESC);
}

echo "$a $b";
?>


<?php
trim(fgets(STDIN));
$prices = explode(' ', trim(fgets(STDIN)));
$pricesDESC = $prices;

sort($prices, SORT_NUMERIC);
rsort($pricesDESC, SORT_NUMERIC);

while ($fruits[] = trim(fgets(STDIN)))

$fruits = array_count_values($fruits);
rsort($fruits, SORT_NUMERIC);

print_r($fruits);

$a = $b = 0;
foreach ($fruits as $num) {
    $a += $num * array_shift($prices);
    $b += $num * array_shift($pricesDESC);
}

echo "$a $b";
?>


t, f = [int(x) for x in raw_input().split()]
prices1 = sorted([int(x) for x in raw_input().split()])
prices2 = prices1[::-1]
fruit = {}
for n in xrange(f):
    s = raw_input()
    if s in fruit:
        fruit[s] += 1
    else:
        fruit[s] = 1
l = []
for k in fruit:
    l.append(fruit[k])
l = sorted(l)[::-1]
low = 0
high = 0
for i, li in enumerate(l):
    low += li * prices1[i]
    high += li * prices2[i]
print low, high


n, m = [int(x) for x in input().split()]
prices = sorted([int(x) for x in input().split()])

fruits = {}

for i in range(m) :
    fruit = input()
    try :
        fruits[fruit] += 1
    except :
        fruits[fruit] = 1

fruits = sorted(fruits.values(), reverse = True)

minm, maxm = 0, 0

for i in range(len(fruits)) :
    minm += fruits[i] * prices[i]
    maxm += fruits[i] * prices[-(i + 1)]

print(minm, maxm, sep = ' ')


from collections import Counter

n,m = map(int,input().split())
prices = list(map(int,input().split()))
fruits = []

for i in range(m):
    fruit = str(input())
    fruits.append(fruit)

freq = Counter(fruits)
prices = sorted(prices)

maximum = 0
minimum = 0

freq.most_common()

i = 0

for fruit in freq:
    maximum += freq[fruit] * prices[~(i)]
    minimum += freq[fruit] * prices[i]

    i += 1

ans = [minimum, maximum]

for i in ans:
    print(i,end=' ')


from collections import Counter

n,m = map(int,input().split())
prices = list(map(int,input().split()))
fruits = []

for i in range(m):
    fruit = str(input())
    fruits.append(fruit)

freq = Counter(fruits)
prices = sorted(prices)

maximum = 0
minimum = 0

freq = sorted(freq.items(), key=lambda x:x[1], reverse = True)

i = 0

for fruit,amnt in freq:
    maximum += amnt * prices[~(i)]
    minimum += amnt * prices[i]

    i += 1

ans = [minimum, maximum]

for i in ans:
    print(i,end=' ')


n,m=map(int,input().split())
a=list(map(int,input().split()))
di={}
for i in range(m):
    word=input()
    di[word]=di.get(word,0)
    di[word]+=1
b=sorted(di.values(),reverse=True)
a=sorted(a)
c=a[::-1]
mini=0
maxi=0
for i in range(len(b)):
    mini+=b[i]*a[i]
    maxi+=b[i]*c[i]
print(mini,maxi)


n,m=map(int,input().split())
m1=list(map(int,input().split()))
l1=[]
l2=[]
for i in range(m):
	l1.append(input())
l3=list(set(l1))
for i in range(len(l3)):
	l2.append(l1.count(l3[i]))
m1.sort()
l2.sort(reverse=True)
j=0
s1=0
for i in range(len(l2)):
	s1=s1+l2[i]*m1[j]
	j=j+1
j=0
s2=0
m1.sort(reverse=True)
for i in range(len(l2)):
	s2=s2+l2[i]*m1[j]
	j=j+1
print(s1,s2)


from itertools import groupby

fruits = []
fruit_qty = []
answer_min = 0
answer_max = 0

price_tags, items = list(map(int, input().split()))
price = sorted(list(map(int, input().split())))

for _ in range(items):
  fruits.append(str(input()))

fruits.sort()
fruit_qty = [len(list(group)) for key, group in groupby(fruits)]
fruit_qty.sort(reverse = True)
#print(fruit_qty)

for i, j in zip(price, fruit_qty):
  answer_min += i * j

for i, j in zip(sorted(price, reverse=True), fruit_qty):
  answer_max += i * j

#print(price, fruits, fruit_qty, answer_min, answer_max)
print(answer_min, answer_max)

"""
fruits = []
shopping_cart = []
total_price = 0
a, b = list(map(int, input().split()))
price = list(map(int, input().split()))

for _ in range(b):
  fruits.append(str(input()))

low = sorted(price)
high = []

for price, fruit in zip(low, fruits):
  if fruit in shopping_cart:
    total_price = 0
    continue

  shopping_cart.append(fruit)

  if fruit in fruits:
    print(fruit, "orange asdas")
  print(price, fruit)

high = sum([i for i in sorted(price, reverse = True)[:b]])
low = sum([i for i in sorted(price)[:b]])
print(low, high)
"""


a, b = list(map(int, input().split()))
price = list(map(int, input().split()))

for _ in range(b):
  str(input())

high = sum([i for i in sorted(price, reverse = True)[:b]])
low = sum([i for i in sorted(price)[:b]])
print(low, high)


from itertools import groupby

fruits = []
fruit_qty = []
answer_min = 0
answer_max = 0

price_tags, items = list(map(int, input().split()))
price = sorted(list(map(int, input().split())))

for _ in range(items):
  fruits.append(str(input()))

fruits.sort()
fruit_qty = [len(list(group)) for key, group in groupby(fruits)]
fruit_qty.sort(reverse = True)

for i, j in zip(price, fruit_qty):
  answer_min += i * j

for i, j in zip(sorted(price, reverse=True), fruit_qty):
  answer_max += i * j

#print(price, fruits, fruit_qty, answer_min, answer_max)
print(answer_min, answer_max)


import sys
from array import array  # noqa: F401
from collections import Counter

def input():
    return sys.stdin.buffer.readline().decode('utf-8')

n, m = map(int, input().split())
a = sorted(map(int, input().split()))
counter = Counter(input().rstrip() for _ in range(m))
vals = sorted(counter.values(), reverse=True)

ans = [
    sum(x * y for x, y in zip(a, vals)),
    sum(x * y for x, y in zip(reversed(a), vals))
]

print(*ans)


import math
import os
import random
import re
import sys
import functools
from operator import itemgetter, attrgetter

if __name__ == '__main__':
    n, m = list(map(int, input().strip().split()))
    a, b, c = list(map(int, input().strip().split())), list(), 1
    r, min_val, max_val = list(), 0, 0

    for i in range(m):
        b.append(input())

    a.sort()
    b.sort()

    for i in range(1, m):
        if b[i] != b[i - 1]:
            r.append((b[i - 1], c))
            c = 1
        else:
            c += 1
        if i == m - 1:
            r.append((b[i], c))

    r.sort(key=itemgetter(1), reverse=True)

    for i in range(len(r)):
        min_val += a[i] * r[i][1]
        max_val += a[n - i - 1] * r[i][1]

    print("{0} {1}".format(min_val, max_val))


import math
import os
import random
import re
import sys
import functools
from operator import itemgetter, attrgetter

if __name__ == '__main__':
    n, m = list(map(int, input().strip().split()))
    a = list(map(int, input().strip().split()))
    d = dict()

    for i in range(m):
        t = input()
        d[t] = d.get(t, 0) + 1

    a.sort()
    d = sorted(d.values(), reverse=True)

    for c in a, a[::-1]:
        print(sum(d[i] * c[i] for i in range(len(d))), end=' ')


import math
import os
import random
import re
import sys
import functools
from operator import itemgetter, attrgetter

if __name__ == '__main__':
    n, m = list(map(int, input().strip().split()))
    a, b, c = list(map(int, input().strip().split())), list(), 1
    r, min_val, max_val = list(), 0, 0

    for i in range(m):
        b.append(input())

    a.sort()
    b.sort()

    if m > 1:
        for i in range(1, m):
            if b[i] != b[i - 1]:
                r.append((b[i - 1], c))
                c = 1
            else:
                c += 1
            if i == m - 1:
                r.append((b[i], c))

        r.sort(key=itemgetter(1), reverse=True)

        for i in range(len(r)):
            min_val += a[i] * r[i][1]
            max_val += a[n - i - 1] * r[i][1]
    else:
        min_val += a[0]
        max_val += a[n - 1]

    print("{0} {1}".format(min_val, max_val))


S=sorted
R=input
I=lambda:map(int,R().split())
n,m=I()
C=S(I())
d={}
for _ in'0'*m:t=R();d[t]=d.get(t,0)+1
d=S(d.values())[::-1]
for c in C,C[::-1]:print(sum(d[i]*c[i]for i in range(len(d))),end=' ')


class A:

    def rotate_keypad(self, keypad):
        reversed_rows = list(reversed(keypad))
        return ["".join(list(reversed(x))) for x in reversed_rows]

    def solve(self):
        keypad = [input(), input(), input()]
        if keypad == self.rotate_keypad(keypad):
            print("YES")
        else:
            print("NO")

class B:

    def solve(self):
        alice = input()
        bob = input()

        if bob[0] == '0' and alice[0] != '0':
            print("WRONG_ANSWER")
            return

        bob = int(bob)

        from itertools import permutations

        smallest = int(alice)
        if smallest == 0:
            if bob == 0:
                print("OK")
            else:
                print("WRONG_ANSWER")
        else:
            smallest = min([int("".join(list(perm))) for perm in permutations(sorted(alice)) if perm[0] != '0'])
            if smallest == bob:
                print("OK")
            else:
                print("WRONG_ANSWER")

class C:

    def solve(self):
        [n, m] = [int(x) for x in input().split(" ")]
        prices = sorted([int(x) for x in input().split(" ")])

        fruits = []
        for i in range(m):
            fruits.append(input())

        from collections import Counter
        frequent_fruits = [f for (f, p) in Counter(fruits).most_common()]

        price_assignment = {}
        for f, p in zip(frequent_fruits, prices):
            price_assignment[f] = p

        smallest_price = sum([price_assignment[f] for f in fruits])

        for f, p in zip(frequent_fruits, list(reversed(prices))):
            price_assignment[f] = p

        largest_price = sum([price_assignment[f] for f in fruits])

        print("{} {}".format(smallest_price, largest_price))

C().solve()


import sys
import math
import itertools
import functools
import collections
import operator
import fileinput
import copy

ORDA = 97  # a
def ii(): return int(input())
def mi(): return map(int, input().split())
def li(): return [int(i) for i in input().split()]
def lcm(a, b): return abs(a * b) // math.gcd(a, b)
def revn(n): return str(n)[::-1]
def dd(): return collections.defaultdict(int)
def ddl(): return collections.defaultdict(list)
def sieve(n):
    if n < 2: return list()
    prime = [True for _ in range(n + 1)]
    p = 3
    while p * p <= n:
        if prime[p]:
            for i in range(p * 2, n + 1, p):
                prime[i] = False
        p += 2
    r = [2]
    for p in range(3, n + 1, 2):
        if prime[p]:
            r.append(p)
    return r
def divs(n, start=2):
    r = []
    for i in range(start, int(math.sqrt(n) + 1)):
        if (n % i == 0):
            if (n / i == i):
                r.append(i)
            else:
                r.extend([i, n // i])
    return r
def divn(n, primes):
    divs_number = 1
    for i in primes:
        if n == 1:
            return divs_number
        t = 1
        while n % i == 0:
            t += 1
            n //= i
        divs_number *= t
def prime(n):
    if n == 2: return True
    if n % 2 == 0 or n <= 1: return False
    sqr = int(math.sqrt(n)) + 1
    for d in range(3, sqr, 2):
        if n % d == 0: return False
    return True
def convn(number, base):
    newnumber = 0
    while number > 0:
        newnumber += number % base
        number //= base
    return newnumber
def cdiv(n, k): return n // k + (n % k != 0)
def ispal(s):
    for i in range(len(s) // 2 + 1):
        if s[i] != s[-i - 1]:
            return False
    return True

n, m = mi()
a = sorted(li())
d = {}
for i in range(m):
    s = input()
    if s in d:
        d[s] += 1
    else:
        d[s] = 1
min_ = 0
t = sorted(d.values(), reverse=True)
j = 0
for i in t:
    min_ += i * a[j]
    j += 1
max_ = 0
j = n - 1
for i in t:
    max_ += i * a[j]
    print(a[j], max_)
    j -= 1
print(min_, max_)


import sys
import math
import itertools
import functools
import collections
import operator
import fileinput
import copy

ORDA = 97  # a
def ii(): return int(input())
def mi(): return map(int, input().split())
def li(): return [int(i) for i in input().split()]
def lcm(a, b): return abs(a * b) // math.gcd(a, b)
def revn(n): return str(n)[::-1]
def dd(): return collections.defaultdict(int)
def ddl(): return collections.defaultdict(list)
def sieve(n):
    if n < 2: return list()
    prime = [True for _ in range(n + 1)]
    p = 3
    while p * p <= n:
        if prime[p]:
            for i in range(p * 2, n + 1, p):
                prime[i] = False
        p += 2
    r = [2]
    for p in range(3, n + 1, 2):
        if prime[p]:
            r.append(p)
    return r
def divs(n, start=2):
    r = []
    for i in range(start, int(math.sqrt(n) + 1)):
        if (n % i == 0):
            if (n / i == i):
                r.append(i)
            else:
                r.extend([i, n // i])
    return r
def divn(n, primes):
    divs_number = 1
    for i in primes:
        if n == 1:
            return divs_number
        t = 1
        while n % i == 0:
            t += 1
            n //= i
        divs_number *= t
def prime(n):
    if n == 2: return True
    if n % 2 == 0 or n <= 1: return False
    sqr = int(math.sqrt(n)) + 1
    for d in range(3, sqr, 2):
        if n % d == 0: return False
    return True
def convn(number, base):
    newnumber = 0
    while number > 0:
        newnumber += number % base
        number //= base
    return newnumber
def cdiv(n, k): return n // k + (n % k != 0)
def ispal(s):
    for i in range(len(s) // 2 + 1):
        if s[i] != s[-i - 1]:
            return False
    return True

n, m = mi()
a = sorted(li())
d = {}
for i in range(m):
    s = input()
    if s in d:
        d[s] += 1
    else:
        d[s] = 1
min_ = 0
t = sorted(d.values(), reverse=True)
j = 0
for i in t:
    min_ += i * a[j]
    j += 1
max_ = 0
j = n - 1
for i in t:
    max_ += i * a[j]
    j -= 1
print(min_, max_)


from collections import defaultdict

N, M = map( int, input().split() )
A = list( map( int, input().split() ) )
A.sort()
minv, maxv = 0, 0

cnt = defaultdict( int )
for i in range( M ):
  fruit = input()
  cnt[ fruit ] += 1

B = [ cnt[ key ] for key in cnt ]
B.sort()

for i in range( len( B ) ):
  minv += A[ i ] * B[ len( B ) - 1 - i ]
  maxv += A[ len( A ) - 1 - i ] * B[ len( B ) - 1 - i ]

print( minv, maxv )


from collections import defaultdict

N, M = map( int, input().split() )
A = list( map( int, input().split() ) )
A.sort()
minv, maxv = 0, 0

cnt = defaultdict( int )
for i in range( M ):
  fruit = input()
  cnt[ fruit ] += 1

B = [ cnt[ key ] for key in cnt ]
B.sort()

for i in range( len( B ) ):
  minv += A[ i ] * B[ len( B ) - 1 - i ]
  maxv += A[ len( A ) - 1 - i ] * B[ i ]

print( minv, maxv )


n, m = tuple(map(int, input().split()))
a = sorted(list(map(int, input().split())))
b = {}
for i in range(m):
    c = input()
    if c in b:
        b[c] += 1
    else:
        b[c] = 1
d = sorted(list(b.values()))
e = 0
f = 0
for i in range(len(d)):
    e += d[-i - 1] * a[i]
    f += d[-i - 1] * a[-i - 1]
print(e, f)


from collections import Counter
n,m = map(int,input().split())
a = sorted(map(int,input().split()))
fruits = Counter([input() for i in range(m)])
fruits = sorted([fruits[x] for x in fruits])
k = len(fruits)
def dot(a,b):
    ans = 0
    for i in range(len(a)):
        ans += a[i]*b[i]
    return ans
hi = dot(fruits,a[-k:])
lo = dot(fruits[::-1],a[:k])
print(lo,hi)


n , k = map(int, input().split())
l = list(map(int, input().split()))
l.sort()
d = {}
for i in range(k):
    t = input()
    if t in d:
        d[t] = d[t] + 1
    else:
        d[t] = 1
dr = sorted(d.items(), key=lambda x: x[1], reverse=True)

min = 0
max = 0

for j in range(len(dr)):
    min = min + int(dr[j][1])*l[j]
l = l[::-1]
for j in range(len(dr)):
    max = max + int(dr[j][1])*l[j]
print(min, max)


ori = input().split(' ')
n = eval(ori[0])
m = eval(ori[1])
price = input().split(' ')
for i in range(n):
    price[i] = eval(price[i])
fruits={}
for i in range(m):
    tmp = input()
    if tmp in fruits:
        fruits[tmp] += 1
    else:
        fruits[tmp] = 1

a = sorted(fruits.items(), key=lambda item: item[1], reverse=True)
b=sorted(price)

#print(a, b)

minp = 0
maxp = 0

for i in range(len(a)):
    minp += a[i][1] * b[i]
    maxp+=a[i][1]*b[len(b)-1-i]
    i+=1

print('{0} {1}'.format(minp,maxp))


import os

import sys

debug = True

if debug and os.path.exists("input.in"):
    input = open("input.in", "r").readline
else:
    debug = False
    input = sys.stdin.readline

def inp():
    return (int(input()))

def inlt():
    return (list(map(int, input().split())))

def insr():
    s = input()
    return s[:len(s) - 1]  # Remove line char from end

def invr():
    return (map(int, input().split()))

test_count = 1
if debug:
    test_count = inp()
for t in range(test_count):
    if debug:
        print("Test Case #", t + 1)
    # Start code here
    n, m = invr()
    a = inlt()
    a.sort()
    fruits = dict()
    for _ in range(m):
        x = insr()
        if x in fruits:
            fruits[x] += 1
        else:
            fruits[x] = 1
    values = sorted(fruits.values(), reverse=True)
    max_total = 0
    min_total = 0
    for i in range(len(values)):
        min_total += values[i] * a[i]
        max_total += values[i] * a[-i - 1]
    print(min_total, max_total)


n,m = map(int,input().split())

t = list(map(int,input().split()))
f=[]

for k in range(m):
    f.append(input())

l=[]

for k in set(f):
    l.append([f.count(k),k])

l.sort()
L=l[::-1]
t.sort()

p=0
q=0
for h in range(len(l)):
    p+=L[h][0]*t[h]
    q+=L[h][0]*t[-(h+1)]

print(p,q)


import sys
import math
import copy
from itertools import permutations as pm
input=sys.stdin.readline
d={}

t=1
while t>0:
    t-=1

    n,m=map(int,input().split())
    d={}
    a=[int(x) for x in input().split()]
    for i in range(m):
        s=input().rstrip()
        if s not in d:
            d[s]=1
        else:
            d[s]+=1
    b=sorted(a)
    c=[]
    for i in d:
        c.append(d[i])
    c.sort(reverse=True)
    x=0;j=0
    for i in c:
        x+=i*b[j]
        j+=1
    print(x,end=" ")
    b=b[::-1]
    x=0;j=0
    for i in c:
        x+=i*b[j]
        j+=1
    print(x,end=" ")


def R(): return map(int, input().split())
def I(): return int(input())
def S(): return str(input())

def L(): return list(R())

from collections import Counter

import math
import sys

n,m=R()

a=L()
a.sort()
d={}
for i in range(m):
    s=S()

    d[s]=1 if not s in d else d[s]+1

v=list(d.values())

v.sort()
min_sum=0
max_sum=0

for i in range(len(d)):
    min_sum+=v[-i-1]*a[i]
    max_sum+=v[i]*a[i-len(v)+len(a)]

print(min_sum,max_sum)


def R(): return map(int, input().split())
def I(): return int(input())
def S(): return str(input())

def L(): return list(R())

from collections import Counter

import math
import sys

n,m=R()

a=L()
a.sort()
d={}
for i in range(m):
    s=S()

    d[s]=1 if not s in d else d[s]+1

v=list(d.values())

v.sort()
min_sum=0
max_sum=0

for i in range(len(d)):
    min_sum+=v[-i]*a[i]
    max_sum+=v[i]*a[i-len(v)+len(a)]

print(min_sum,max_sum)


#pragma region include

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<iostream>
#include<sstream>
#include<queue>
#include<math.h>
#include<stdlib.h>
#include<stack>
#include<string.h>
#include<string>
#include<map>
#include<algorithm>
#include<time.h>
#include<set>
#include<vector>
#include<numeric>
#include<iomanip>

#include <bitset>
#include <functional>
using namespace std;

#pragma endregion

#pragma region define
#define All(a) a.begin(), a.end()
#define _2d(a,row,col,type) type**a=new type*[row]; for (int i=0;i<row;i++) a[i]=new type[col];
#define rep(i,n) for(int i=0;i<int(n);i++)
#define repd(i,n) for(int i=n-1;i>=0;i--)
#define repi(i,a,n) for(int i=int(a);i<int(n);i++)
#define clr(a, n) memset(a, n, sizeof(a));
#define scn(a,n) rep(i,n) cin>>a[i];
#define scn2(a,row,col) rep(i,row) rep(j,col) cin>>a[i][j];
#define prn(a,n) rep(i,n-1) cout<<a[i]<<" "; cout<<a[n-1]<<endl;
#define prn2(a,row,col) rep(i,row){rep(j,col-1) cout<<a[i][j]<<" "; cout<<a[i][col-1]<<endl;}
#define mp make_pair
#define Odd(x) x%2!=0
#define Even(x) x%2==0
#define Pi 3.14159265358979323846
#define INF 2000000000 // 2 billion
#define read freopen("in.in","r",stdin)
#define write freopen("out.out","w",stdout)
#define IOS  ios::sync_with_stdio(false)
#define MOD (1e9 + 7)
#define SQR(x) (x) * (x)
#pragma endregion

#pragma region typedef

typedef long long ll;
typedef unsigned long ul;
typedef unsigned long long ull;
typedef long double ld;
typedef vector<int> vi;
typedef vector<vi > vivi;
typedef vector<long> vl;
typedef vector<ll> vll;
typedef vector<double> vd;
typedef vector<string> vs;
typedef pair<int, string> is;
typedef pair<ll, ll> pii;
typedef pair<pair<int, int>, ll> pi;

#pragma endregion

int nn, mm;
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
int lcm(int a, int b) { return a * (b / gcd(a, b)); }
bool in_range(int i, int j){ return (i >= 0 && i < nn && j >= 0 && j < mm); }

/********************************/

int main(){
	int n,a,b,t; cin >> n>>a>>b>>t;
	string s; cin >> s;
	int res = 0;
	int temp = t;
	if (s[0] == 'w') t -= b;
	t--;
	if (t < 0) return cout << "0", 0;
	//t = temp;
	vi tl(n+1,0), tr(n+1,0);
	temp = 0;
	repi(i, 1, n+1){
		temp += a + 1;
		if (s[i] == 'w') temp += b;
		tr[i] = temp;
	}

	temp = 0;
	repd(i, n){
		temp += a + 1;
		if (s[i] == 'w') temp += b;
		tl[i] = temp;
	}

	int posR = 1, posL = n-1;

	repi(i,1, n){
		if (tr[i] > t) break;
		while (posR < n && tl[posR] + tr[i] + (i) * a > t) posR++;
		res = max(res, 1 + i + n - posR);
		if (res >= n) break;
	}

	repd(i, n){
		if (tl[i] > t) break;
		while (posL > 0 && tr[posL] + tl[i] + (n-i) * a > t) posL--;
		res = max(res, 1 + n + posL - i);
		if (res >= n) break;
	}
	if (res > n) res = n;
	cout << res;
	return 0;
}

/*

*/


#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<set>
#include<cstdio>
#include<utility>
#include<math.h>

#define ll long long
#define MAXN 100100
#define ost 1000000007;
#define _USE_MATH_DEFINES

using namespace std;

struct point
{
	ll x;
	ll y;
	point(ll a = 0, ll b = 0){
		x = a;
		y = b;
	}
};

double sqr(double a){ return a*a; }

bool cmp(ll a, ll b){
	return a<b;
}

int inc(int j, int n)
{
	j++;
	if(j==n) j = 0;
	return j;
}

int dec(int j, int n)
{
	j--;
	if(j<0) j = n - 1;
	return j;
}

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	int n, a, b, t, h, p, l, r, m = 0;
	cin >> n >> a >> b >> t;
	h = t;
	string s;
	cin >> s;
	l = 0;
	r = 0;
	p = 0;
	for (int i = 0; i < n; i++)
	{
		if (l != 0)
		{
			t += a + 1;
			if (s[i-1] == 'w') t += b;
			l--;
		}
		else
		{
			r = i;
		}
		p = 0;
		while (p <= t)
		{
			if (s[r] == 'w')
			{
				p = b + 1;
			}
			else p = 1;
			if (p + a <= t)
			{
				t -= p + a;
				r = inc(r,n);
				l++;
				if (l == n)
				{
					cout << n;
					return 0;
				}
				p = 0;
			}
			else
			{
				if (p <= t)
				{
					m = max(l+ 1, m);
					if (m == n)
					{
						cout << m;
						return 0;
					}
					break;
				}
				else
				{
					m = max(l, m);
					if (m == n)
					{
						cout << m;
						return 0;
					}
				}
			}
		}
	}

	l = 0, r = n-1, p = 0;
	t = h;
	for (int i = n-1; i >=0; i--)
	{
		if (l != 0)
		{
			t += a + 1;
			if (s[i+1] == 'w') t += b;
			l--;
		}
		else
		{
			r = i;
		}
		p = 0;
		while (p <= t)
		{
			if (s[r] == 'w')
			{
				p = b + 1;
			}
			else p = 1;
			if (p + a <= t)
			{
				t -= p + a;
				r = dec(r, n);
				l++;
				if (l == n)
				{
					cout << n;
					return 0;
				}
				p = 0;
			}
			else
			{
				if (p <= t)
				{
					m = max(l + 1, m);
					if (m == n)
					{
						cout << m;
						return 0;
					}
					break;
				}
				else
				{
					m = max(l, m);
					if (m == n)
					{
						cout << m;
						return 0;
					}
				}
			}
		}
	}
	cout << m;
	return 0;
}


#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<set>
#include<cstdio>
#include<utility>
#include<math.h>

#define ll long long
#define MAXN 100100
#define ost 1000000007;
#define _USE_MATH_DEFINES

using namespace std;

struct point
{
	ll x;
	ll y;
	point(ll a = 0, ll b = 0){
		x = a;
		y = b;
	}
};

double sqr(double a){ return a*a; }

bool cmp(ll a, ll b){
	return a<b;
}

int inc(int j, int n)
{
	j++;
	if(j==n) j = 0;
	return j;
}

int dec(int j, int n)
{
	j--;
	if(j<0) j = n - 1;
	return j;
}

int binsearch(int t, int l, int r,const vector<long long> & x)
{
	int m = (r + l) / 2;
	if (t > x[m])
	{
		return binsearch(t, l, m - 1, x);
	}
	if (t < x[m])
	{
		if (t >= x[m + 1]) return m + 1;
		return binsearch(t, m+1, r, x);
	}
	if (t == x[m])
	{
		return m;
	}
}

int binsearch1(int t, int l, int r, const vector<long long> & x)
{
	int m = (r + l) / 2;
	if (t > x[m])
	{
		return binsearch(t, m+1, r, x);
	}
	if (t < x[m])
	{
		if (t >= x[m -1]) return m - 1;
		return binsearch(t, l, m-1, x);
	}
	if (t == x[m])
	{
		return m;
	}
}

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	int n, a, b, t, h, p, l, r, m = 0;
	cin >> n >> a >> b >> t;
	h = t;
	string s;
	cin >> s;
	l = 0;
	r = 0;
	p = 0;
	vector<int> x(n);
	for (int i = 0; i < n; i++)
	{
		if (s[i] == 'w')
		{
			x[i] = b + 1;
		}
		else
		{
			x[i] = 1;
		}
	}
	vector<int> y(n), z(n);
	z[0] = y[0] = 0;
	if (t < x[0])
	{
		cout << 0;
		return 0;
	}
	for (int i = 1; i < n; i++)
	{
		y[i] = y[i - 1] + x[i] + a;
	}
	for (int i = n - 1; i > 0; i--)
	{
		int j = (i + 1) % n;
		z[i] = z[j] + a + x[i];
	}
	m = 1;
	t -= x[0];
	for (int i = 1; i < n; i++)
	{
		if (t>=x[i] + a)
		{
			t -= (x[i] + a);
		}
		else
		{
			break;
		}
		if (t >= i*a)
		{
			p = t - i*a;
			if (p >= z[i + 1])
			{
				cout << n;
				return 0;
			}
			if (p >= z[n - 1])
			{
				int k = binsearch(p, i + 1, n - 1, z);
				m = max(i + 1 + n - k, m);
			}
			else
			{
				m = max(m, i + 1);
			}
		}
		else
		{
			m = max(m, i + 1);
		}
	}
	t = h - x[0];
	for (int i = n - 1; i > 0; i--)
	{
		if (t>=x[i] + a)
		{
			t -= (x[i] + a);
		}
		else
		{
			break;
		}
		if (t >= (n-i)*a)
		{
			p = t - (n-i)*a;
			if (p >= y[i - 1])
			{
				cout << n;
				return 0;
			}
			if (p >= y[1])
			{
				int k = binsearch1(p, 1, i - 1, y);
				m = max(n - i + 1 +  k, m);
			}
			else
			{
				m = max(m, n - i + 1);
			}
		}
		else
		{
			m = max(m, n - i + 1);
		}
	}
	cout << m;
	return 0;
}


#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<set>
#include<cstdio>
#include<utility>
#include<math.h>

#define ll long long
#define MAXN 100100
#define ost 1000000007;
#define _USE_MATH_DEFINES

using namespace std;

struct point
{
	ll x;
	ll y;
	point(ll a = 0, ll b = 0){
		x = a;
		y = b;
	}
};

double sqr(double a){ return a*a; }

bool cmp(ll a, ll b){
	return a<b;
}

int inc(int j, int n)
{
	j++;
	if(j==n) j = 0;
	return j;
}

int dec(int j, int n)
{
	j--;
	if(j<0) j = n - 1;
	return j;
}

int binsearch(int t, int l, int r,const vector<long long> & x)
{
	int m = (r + l) / 2;
	if (t > x[m])
	{
		return binsearch(t, l, m - 1, x);
	}
	if (t < x[m])
	{
		if (t >= x[m + 1]) return m + 1;
		return binsearch(t, m+1, r, x);
	}
	if (t == x[m])
	{
		return m;
	}
}

int binsearch1(int t, int l, int r, const vector<long long> & x)
{
	int m = (r + l) / 2;
	if (t > x[m])
	{
		return binsearch(t, m+1, r, x);
	}
	if (t < x[m])
	{
		if (t >= x[m -1]) return m - 1;
		return binsearch(t, l, m-1, x);
	}
	if (t == x[m])
	{
		return m;
	}
}

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	int n, a, b, t, h, p, l, r, m = 0;
	cin >> n >> a >> b >> t;
	h = t;
	string s;
	cin >> s;
	l = 0;
	r = 0;
	p = 0;
	vector<int> x(n);
	for (int i = 0; i < n; i++)
	{
		if (s[i] == 'w')
		{
			x[i] = b + 1;
		}
		else
		{
			x[i] = 1;
		}
	}
	vector<long long> y(n), z(n);
	z[0] = y[0] = 0;
	if (t < x[0])
	{
		cout << 0;
		return 0;
	}
	for (int i = 1; i < n; i++)
	{
		y[i] = y[i - 1] + x[i] + a;
	}
	for (int i = n - 1; i > 0; i--)
	{
		int j = (i + 1) % n;
		z[i] = z[j] + a + x[i];
	}
	m = 1;
	t -= x[0];
	for (int i = 1; i < n; i++)
	{
		if (t>=x[i] + a)
		{
			t -= (x[i] + a);
		}
		else
		{
			break;
		}
		if (t >= i*a)
		{
			p = t - i*a;
			if (p >= z[i + 1])
			{
				cout << n;
				return 0;
			}
			if (p >= z[n - 1])
			{
				int k = binsearch(p, i + 1, n - 1, z);
				m = max(i + 1 + n - k, m);
			}
			else
			{
				m = max(m, i + 1);
			}
		}
		else
		{
			m = max(m, i + 1);
		}
	}
	t = h - x[0];
	for (int i = n - 1; i > 0; i--)
	{
		if (t>=x[i] + a)
		{
			t -= (x[i] + a);
		}
		else
		{
			break;
		}
		if (t >= (n-i)*a)
		{
			p = t - (n-i)*a;
			if (p >= y[i - 1])
			{
				cout << n;
				return 0;
			}
			if (p >= y[1])
			{
				int k = binsearch1(p, 1, i - 1, y);
				m = max(n - i + 1 +  k, m);
			}
			else
			{
				m = max(m, n - i + 1);
			}
		}
		else
		{
			m = max(m, n - i + 1);
		}
	}
	cout << m;
	return 0;
}