using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Numerics;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
using System.IO;
public class CodeForces
{
#if TEST
    // To set this go to Project -> Properties -> Build -> General -> Conditional compilation symbols: -> enter 'TEST' into text box.
    const bool testing = true;
#else
const bool testing = false;
#endif

    private static int Next()
    {
        int c;
        int res = 0;
        do
        {
            c = reader.Read();
            if (c == -1)
                return res;
        } while (c < '0' || c > '9');
        res = c - '0';
        while (true)
        {
            c = reader.Read();
            if (c < '0' || c > '9')
                return res;
            res *= 10;
            res += c - '0';
        }
    }
    private static readonly StreamReader reader = new StreamReader(Console.OpenStandardInput(1024 * 10), Encoding.ASCII, false, 1024 * 10);
    private static readonly StreamWriter writer = new StreamWriter(Console.OpenStandardOutput(1024 * 10), Encoding.ASCII, 1024 * 10);

    struct Node : IComparable
    {
        public long s { get; set; }
        public long a { get; set; }
        public long b { get; set; }
        public long diff { get; set; }
        public int CompareTo(object obj)
        {
            return this.diff.CompareTo(((Node)obj).diff);
        }
    }

    static long calculate(long value)
    {
        var slices = S * value;
        var other = (minNumber - value) * S;

        var i = 0;
        long result = 0;
        while(slices >= 0 && i < list.Count)
        {
            var koof = Math.Min(slices, list[i].s);
            result +=  koof* list[i].b;
            slices -= list[i].s;
            if (slices < 0)
            {
                other -= slices*-1;
                result += slices * -1 * list[i].a;
            }
            i++;
        }
        for (; i < list.Count; i++)
        {
            result += list[i].s * list[i].a;
            other -= list[i].s;
        }

        return result;
    }
    static long calculateBack(long value)
    {
        var slices = (minNumber - value) * S;

        var i = list.Count-1;
        long result = 0;
        while (slices >= 0 && i >=0)
        {
            var koof = Math.Min(slices, list[i].s);
            result += koof * list[i].a;
            slices -= list[i].s;
            if (slices < 0)
            {
                result += slices * -1 * list[i].b;
            }
            i--;
        }
        for (; i>=0; i--)
        {
            result += list[i].s * list[i].b;
        }

        return result;
    }
    static List<Node> list { get; set; }
    static long S;
    static long max = 0;
    static long minNumber = 0;
    static void program(TextReader input)
    {
        var data = input.ReadLine().Split(' ').Select(long.Parse).ToList();
        var N = data[0];
        S = (long)data[1];
        list = new List<Node>();
        for(var i = 0; i < N; i++)
        {
            data = input.ReadLine().Split(' ').Select(long.Parse).ToList();
            list.Add(new Node()
            {
                s = data[0],
                a = data[1],
                b = data[2],
                diff = data[1] - data[2]
            });
            max = Math.Max(max, data[1]);
            max = Math.Max(max, data[2]);
        }
        list.Sort((a2,b2) => a2.CompareTo(b2));
        var a = 0l;

        long b = (long)Math.Ceiling(list.Select(it => it.s).Sum() / (double)S);
        minNumber = b;
        //data = Console.ReadLine().TrimEnd().Split(' ').Select(long.Parse).ToList();
        var f = calculate(a);
        long min = Math.Max(f, calculate(b));
        for (var i = 0; i < 300; i++)
        {
            if (b < a)
            {
                break;
            }
            double mid = (b - a) / 3.0;
            long left = a + (b - a) / 3;
            long right = b - (b - a) / 3;
            long leftVal = Math.Max(calculate(left), calculateBack(left));
            var rightVal = Math.Max(calculate(right), calculateBack(right));
            min = Math.Max(min, leftVal);
            min = Math.Max(min, rightVal);
            if (leftVal >= rightVal)
            {
                b = right-1;
            }
            else
            {
                a = left+1;
            }
        }
        a = 0l;

        b = (long)Math.Ceiling(list.Select(it => it.s).Sum() / (double)S);
        minNumber = b;
        //data = Console.ReadLine().TrimEnd().Split(' ').Select(long.Parse).ToList();
        f = calculateBack(a);
        min = Math.Max(Math.Max(min, f), calculateBack(b));
        for (var i = 0; i < 300; i++)
        {
            if (b < a)
            {
                break;
            }
            double mid = (b - a) / 3.0;
            long left = a + (b - a) / 3;
            long right = b - (b - a) / 3;
            long leftVal = calculateBack(left);
            var rightVal = calculateBack(right);
            min = Math.Max(min, leftVal);
            min = Math.Max(min, rightVal);
            if (leftVal >= rightVal)
            {
                b = right - 1;
            }
            else
            {
                a = left + 1;
            }
        }

        writer.WriteLine(min);
        writer.Flush();
    }

    public static void Main(string[] args)
    {
        if (!testing)
        { // set testing to false when submiting to codeforces
            program(Console.In); // write your program in 'program' function (its your new main !)
            return;
        }

        //Console.WriteLine("Test Case(1) => expected :");
        //Console.WriteLine("84\n");
        //Console.WriteLine("Test Case(1) => found    :");
        //program(new StringReader("3 12\n3 5 7\n4 6 7\n5 9 5\n"));
        //Console.WriteLine();
        //Console.ReadLine();

        Console.WriteLine("Test Case(2) => expected :");
        Console.WriteLine("314\n");
        Console.WriteLine("Test Case(2) => found    :");
        program(new StringReader("6 10\n7 4 7\n5 8 8\n12 5 8\n6 11 6\n3 3 7\n5 9 6\n"));
        Console.WriteLine();
        Console.ReadLine();

    }
}


using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Numerics;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
using System.IO;
public class CodeForces
{
#if TEST
    // To set this go to Project -> Properties -> Build -> General -> Conditional compilation symbols: -> enter 'TEST' into text box.
    const bool testing = true;
#else
const bool testing = false;
#endif

    private static int Next()
    {
        int c;
        int res = 0;
        do
        {
            c = reader.Read();
            if (c == -1)
                return res;
        } while (c < '0' || c > '9');
        res = c - '0';
        while (true)
        {
            c = reader.Read();
            if (c < '0' || c > '9')
                return res;
            res *= 10;
            res += c - '0';
        }
    }
    private static readonly StreamReader reader = new StreamReader(Console.OpenStandardInput(1024 * 10), Encoding.ASCII, false, 1024 * 10);
    private static readonly StreamWriter writer = new StreamWriter(Console.OpenStandardOutput(1024 * 10), Encoding.ASCII, 1024 * 10);

    struct Node : IComparable
    {
        public long s { get; set; }
        public long a { get; set; }
        public long b { get; set; }
        public long diff { get; set; }
        public int CompareTo(object obj)
        {
            return this.diff.CompareTo(((Node)obj).diff);
        }
    }

    static long calculate(long value)
    {
        var slices = S * value;
        var other = (minNumber - value) * S;

        var i = 0;
        long result = 0;
        while(slices >= 0 && i < list.Count && list[i].diff < 0)
        {
            var koof = Math.Min(slices, list[i].s);
            result +=  koof* list[i].b;
            slices -= list[i].s;
            if (slices < 0)
            {
                other -= slices*-1;
                result += slices * -1 * list[i].a;
            }
            i++;
        }
        var i2 = list.Count - 1;
        for (; i < list.Count; i++)
        {
            result += list[i].s * list[i].a;
            other -= list[i].s;
        }

        return result;
    }
    static long calculateBack(long value)
    {
        var slices = (minNumber - value) * S;

        var i = list.Count-1;
        long result = 0;
        while (slices >= 0 && i >=0)
        {
            var koof = Math.Min(slices, list[i].s);
            result += koof * list[i].a;
            slices -= list[i].s;
            if (slices < 0)
            {
                result += slices * -1 * list[i].b;
            }
            i--;
        }
        for (; i>=0; i--)
        {
            result += list[i].s * list[i].b;
        }

        return result;
    }
    static List<Node> list { get; set; }
    static long S;
    static long max = 0;
    static long minNumber = 0;
    static void program(TextReader input)
    {
        var data = input.ReadLine().Split(' ').Select(long.Parse).ToList();
        var N = data[0];
        S = (long)data[1];
        list = new List<Node>();
        for(var i = 0; i < N; i++)
        {
            data = input.ReadLine().Split(' ').Select(long.Parse).ToList();
            list.Add(new Node()
            {
                s = data[0],
                a = data[1],
                b = data[2],
                diff = data[1] - data[2]
            });
            max = Math.Max(max, data[1]);
            max = Math.Max(max, data[2]);
        }
        list.Sort((a2,b2) => a2.CompareTo(b2));
        var a = 0l;

        long b = (long)Math.Ceiling(list.Select(it => it.s).Sum() / (double)S);
        minNumber = b;
        //data = Console.ReadLine().TrimEnd().Split(' ').Select(long.Parse).ToList();
        var f = calculate(a);
        long min = Math.Max(f, calculate(b));
        for (var i = 0; i < 300; i++)
        {
            if (b < a)
            {
                break;
            }
            double mid = (b - a) / 3.0;
            long left = a + (b - a) / 3;
            long right = b - (b - a) / 3;
            long leftVal = calculate(left);
            var rightVal = calculate(right);
            min = Math.Max(min, leftVal);
            min = Math.Max(min, rightVal);
            if (leftVal >= rightVal)
            {
                b = right-1;
            }
            else
            {
                a = left+1;
            }
        }
        a = 0l;

        b = (long)Math.Ceiling(list.Select(it => it.s).Sum() / (double)S);
        minNumber = b;
        //data = Console.ReadLine().TrimEnd().Split(' ').Select(long.Parse).ToList();
        f = calculateBack(a);
        min = Math.Max(Math.Max(min, f), calculateBack(b));
        for (var i = 0; i < 300; i++)
        {
            if (b < a)
            {
                break;
            }
            double mid = (b - a) / 3.0;
            long left = a + (b - a) / 3;
            long right = b - (b - a) / 3;
            long leftVal = calculateBack(left);
            var rightVal = calculateBack(right);
            min = Math.Max(min, leftVal);
            min = Math.Max(min, rightVal);
            if (leftVal >= rightVal)
            {
                b = right - 1;
            }
            else
            {
                a = left + 1;
            }
        }

        writer.WriteLine(min);
        writer.Flush();
    }

    public static void Main(string[] args)
    {
        if (!testing)
        { // set testing to false when submiting to codeforces
            program(Console.In); // write your program in 'program' function (its your new main !)
            return;
        }

        //Console.WriteLine("Test Case(1) => expected :");
        //Console.WriteLine("84\n");
        //Console.WriteLine("Test Case(1) => found    :");
        //program(new StringReader("3 12\n3 5 7\n4 6 7\n5 9 5\n"));
        //Console.WriteLine();
        //Console.ReadLine();

        Console.WriteLine("Test Case(2) => expected :");
        Console.WriteLine("195\n");
        Console.WriteLine("Test Case(2) => found    :");
        program(new StringReader("2 10\n8 9 12\n9 11 9\n"));
        Console.WriteLine();
        Console.ReadLine();

    }
}


using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Numerics;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
using System.IO;
public class CodeForces
{
#if TEST
    // To set this go to Project -> Properties -> Build -> General -> Conditional compilation symbols: -> enter 'TEST' into text box.
    const bool testing = true;
#else
const bool testing = false;
#endif

    private static int Next()
    {
        int c;
        int res = 0;
        do
        {
            c = reader.Read();
            if (c == -1)
                return res;
        } while (c < '0' || c > '9');
        res = c - '0';
        while (true)
        {
            c = reader.Read();
            if (c < '0' || c > '9')
                return res;
            res *= 10;
            res += c - '0';
        }
    }
    private static readonly StreamReader reader = new StreamReader(Console.OpenStandardInput(1024 * 10), Encoding.ASCII, false, 1024 * 10);
    private static readonly StreamWriter writer = new StreamWriter(Console.OpenStandardOutput(1024 * 10), Encoding.ASCII, 1024 * 10);

    struct Node : IComparable
    {
        public long s { get; set; }
        public long a { get; set; }
        public long b { get; set; }
        public long diff { get; set; }
        public int CompareTo(object obj)
        {
            return this.diff.CompareTo(((Node)obj).diff);
        }
    }

    static long calculate(long value)
    {
        var slices = S * value;
        var other = (minNumber - value) * S;

        var i = 0;
        long result = 0;
        while(slices >= 0 && i < list.Count && list[i].diff <= 0)
        {
            var koof = Math.Min(slices, list[i].s);
            result +=  koof* list[i].b;
            slices -= list[i].s;
            if (slices < 0)
            {
                other -= slices*-1;
                result += slices * -1 * list[i].a;
            }
            i++;
        }
        var i2 = list.Count - 1;
        while (i2 >= i && other >=0 && list[i2].diff >= 0){
            result += list[i2].a * Math.Min(other, list[i2].s);
            other -= list[i2].s;
            if(other < 0)
            {
                slices -= other * -1;
                result += other * -1 * list[i2].b;
            }
            i2--;
        }
        while (i <= i2)
        {
            var cur = list[i];
            if(slices > 0)
            {
                result += Math.Min(slices, cur.s) * cur.b;
                slices -= cur.s;
                if (slices < 0)
                {
                    other -= slices * -1;
                    result += slices * -1 * list[i].a;
                }
            } else
            {
                result += cur.s * cur.a;
            }
            i++;
        }

        return result;
    }
    static List<Node> list { get; set; }
    static long S;
    static long max = 0;
    static long minNumber = 0;
    static void program(TextReader input)
    {
        var data = input.ReadLine().Split(' ').Select(long.Parse).ToList();
        var N = data[0];
        S = (long)data[1];
        list = new List<Node>();
        for(var i = 0; i < N; i++)
        {
            data = input.ReadLine().Split(' ').Select(long.Parse).ToList();
            list.Add(new Node()
            {
                s = data[0],
                a = data[1],
                b = data[2],
                diff = data[1] - data[2]
            });
            max = Math.Max(max, data[1]);
            max = Math.Max(max, data[2]);
        }
        list.Sort((a2,b2) => a2.CompareTo(b2));
        var a = 0l;

        long b = (long)Math.Ceiling(list.Select(it => it.s).Sum() / (double)S);
        minNumber = b;
        //data = Console.ReadLine().TrimEnd().Split(' ').Select(long.Parse).ToList();
        var f = calculate(a);
        long min = Math.Max(f, calculate(b));
        for (var i = 0; i < 300; i++)
        {
            if (b < a)
            {
                break;
            }
            double mid = (b - a) / 3.0;
            long left = a + (b - a) / 3;
            long right = b - (b - a) / 3;
            long leftVal = calculate(left);
            var rightVal = calculate(right);
            min = Math.Max(min, leftVal);
            min = Math.Max(min, rightVal);
            if (leftVal >= rightVal)
            {
                b = right-1;
            }
            else
            {
                a = left+1;
            }
        }
        a = 0l;

        b = (long)Math.Ceiling(list.Select(it => it.s).Sum() / (double)S);
        minNumber = b;
        //data = Console.ReadLine().TrimEnd().Split(' ').Select(long.Parse).ToList();

        writer.WriteLine(min);
        writer.Flush();
    }

    public static void Main(string[] args)
    {
        if (!testing)
        { // set testing to false when submiting to codeforces
            program(Console.In); // write your program in 'program' function (its your new main !)
            return;
        }

        Console.WriteLine("Test Case(1) => expected :");
        Console.WriteLine("84\n");
        Console.WriteLine("Test Case(1) => found    :");
        program(new StringReader("3 12\n3 5 7\n4 6 7\n5 9 5\n"));
        Console.WriteLine();
        Console.ReadLine();

        Console.WriteLine("Test Case(2) => expected :");
        Console.WriteLine("195\n");
        Console.WriteLine("Test Case(2) => found    :");
        program(new StringReader("2 10\n8 9 12\n9 11 9\n"));
        Console.WriteLine();
        Console.ReadLine();

    }
}


using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Numerics;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
using System.IO;
public class CodeForces
{
#if TEST
    // To set this go to Project -> Properties -> Build -> General -> Conditional compilation symbols: -> enter 'TEST' into text box.
    const bool testing = true;
#else
const bool testing = false;
#endif

    private static int Next()
    {
        int c;
        int res = 0;
        do
        {
            c = reader.Read();
            if (c == -1)
                return res;
        } while (c < '0' || c > '9');
        res = c - '0';
        while (true)
        {
            c = reader.Read();
            if (c < '0' || c > '9')
                return res;
            res *= 10;
            res += c - '0';
        }
    }
    private static readonly StreamReader reader = new StreamReader(Console.OpenStandardInput(1024 * 10), Encoding.ASCII, false, 1024 * 10);
    private static readonly StreamWriter writer = new StreamWriter(Console.OpenStandardOutput(1024 * 10), Encoding.ASCII, 1024 * 10);

    struct Node : IComparable
    {
        public long s { get; set; }
        public long a { get; set; }
        public long b { get; set; }
        public long diff { get; set; }
        public int CompareTo(object obj)
        {
            return this.diff.CompareTo(((Node)obj).diff);
        }
    }

    static long calculate(long value)
    {
        var slices = S * value;
        var other = (minNumber - value) * S;

        var i = 0;
        long result = 0;
        while(slices >= 0 && i < list.Count && list[i].diff <= 0)
        {
            var koof = Math.Min(slices, list[i].s);
            result +=  koof* list[i].b;
            slices -= list[i].s;
            if (slices < 0)
            {
                other -= slices*-1;
                result += slices * -1 * list[i].a;
            }
            i++;
        }
        var i2 = list.Count - 1;
        while (i2 >= i && other >=0 && list[i2].diff >= 0){
            result += list[i].a * Math.Min(other, list[i].s);
            other -= list[i].s;
            if(other < 0)
            {
                slices -= other * -1;
                result += other * -1 * list[i].b;
            }
            i2--;
        }
        while (i < i2)
        {
            var cur = list[i];
            if(slices > 0)
            {
                result += Math.Min(slices, cur.s) * cur.b;
                slices -= cur.s;
            } else
            {
                result += cur.s * cur.a;
            }
            i++;
        }

        return result;
    }
    static long calculateBack(long value)
    {
        var slices = (minNumber - value) * S;

        var i = list.Count-1;
        long result = 0;
        while (slices >= 0 && i >=0)
        {
            var koof = Math.Min(slices, list[i].s);
            result += koof * list[i].a;
            slices -= list[i].s;
            if (slices < 0)
            {
                result += slices * -1 * list[i].b;
            }
            i--;
        }
        for (; i>=0; i--)
        {
            result += list[i].s * list[i].b;
        }

        return result;
    }
    static List<Node> list { get; set; }
    static long S;
    static long max = 0;
    static long minNumber = 0;
    static void program(TextReader input)
    {
        var data = input.ReadLine().Split(' ').Select(long.Parse).ToList();
        var N = data[0];
        S = (long)data[1];
        list = new List<Node>();
        for(var i = 0; i < N; i++)
        {
            data = input.ReadLine().Split(' ').Select(long.Parse).ToList();
            list.Add(new Node()
            {
                s = data[0],
                a = data[1],
                b = data[2],
                diff = data[1] - data[2]
            });
            max = Math.Max(max, data[1]);
            max = Math.Max(max, data[2]);
        }
        list.Sort((a2,b2) => a2.CompareTo(b2));
        var a = 0l;

        long b = (long)Math.Ceiling(list.Select(it => it.s).Sum() / (double)S);
        minNumber = b;
        //data = Console.ReadLine().TrimEnd().Split(' ').Select(long.Parse).ToList();
        var f = calculate(a);
        long min = Math.Max(f, calculate(b));
        for (var i = 0; i < 300; i++)
        {
            if (b < a)
            {
                break;
            }
            double mid = (b - a) / 3.0;
            long left = a + (b - a) / 3;
            long right = b - (b - a) / 3;
            long leftVal = calculate(left);
            var rightVal = calculate(right);
            min = Math.Max(min, leftVal);
            min = Math.Max(min, rightVal);
            if (leftVal >= rightVal)
            {
                b = right-1;
            }
            else
            {
                a = left+1;
            }
        }
        a = 0l;

        b = (long)Math.Ceiling(list.Select(it => it.s).Sum() / (double)S);
        minNumber = b;
        //data = Console.ReadLine().TrimEnd().Split(' ').Select(long.Parse).ToList();
        f = calculateBack(a);
        min = Math.Max(Math.Max(min, f), calculateBack(b));
        for (var i = 0; i < 300; i++)
        {
            if (b < a)
            {
                break;
            }
            double mid = (b - a) / 3.0;
            long left = a + (b - a) / 3;
            long right = b - (b - a) / 3;
            long leftVal = calculateBack(left);
            var rightVal = calculateBack(right);
            min = Math.Max(min, leftVal);
            min = Math.Max(min, rightVal);
            if (leftVal >= rightVal)
            {
                b = right - 1;
            }
            else
            {
                a = left + 1;
            }
        }

        writer.WriteLine(min);
        writer.Flush();
    }

    public static void Main(string[] args)
    {
        if (!testing)
        { // set testing to false when submiting to codeforces
            program(Console.In); // write your program in 'program' function (its your new main !)
            return;
        }

        Console.WriteLine("Test Case(1) => expected :");
        Console.WriteLine("84\n");
        Console.WriteLine("Test Case(1) => found    :");
        program(new StringReader("3 12\n3 5 7\n4 6 7\n5 9 5\n"));
        Console.WriteLine();
        Console.ReadLine();

        Console.WriteLine("Test Case(2) => expected :");
        Console.WriteLine("195\n");
        Console.WriteLine("Test Case(2) => found    :");
        program(new StringReader("2 10\n8 9 12\n9 11 9\n"));
        Console.WriteLine();
        Console.ReadLine();

    }
}


import java.io.BufferedReader
import java.io.InputStream
import java.io.InputStreamReader
import java.util.*
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min

val MOD = 1_000_000_007L

data class Contestant(val v: Long, val cnt: Long)
class Solver(stream: InputStream, private val out: java.io.PrintWriter) {
  fun solve() {
    val N = ni()
    val S = ni()

    var sum = 0L
    val C = Array(N){
      val s = nl()
      val a = nl()
      val b = nl()
      sum += a * s
      Contestant(b - a, s)
    }

    val lst = C.map{it.cnt}.sum() % S
    debug{"sum:$sum all:${C.map{it.cnt}.sum()} lst:$lst"}

    C.sortBy { - it.v }
    debug{C.joinToString(" ")}

    var ans = sum
    var eaten = 0L
    var toGain = 0L
    for (i in C) {
      var cnt = i.cnt
      if (eaten < lst  && i.cnt >= lst - eaten) {
        val eat = lst - eaten
        sum += toGain + eat * i.v
        ans = max(sum, ans)
        eaten += eat
        toGain = 0
        cnt -= eat
      }

      debug{"LAST $toGain $eaten $sum"}

      if (cnt >= S - eaten) {
        val eat = S - eaten
        sum += toGain + eat * i.v
        ans = max(sum, ans)
        eaten = 0
        toGain = 0
        cnt -= eat
      }

      debug{"S $toGain $eaten $sum"}

      if (eaten == 0L) {
        val c = cnt / S
        sum += c * S * i.v
        ans = max(sum, ans)
        cnt -= c * S
      }

      if (cnt > 0) {
        eaten += cnt
        toGain += cnt * i.v
      }

      debug{"$toGain $eaten $sum"}
    }
    out.println(ans)
  }

  private val isDebug = try {
    // なんか本番でエラーでる
    System.getenv("MY_DEBUG") != null
  } catch (t: Throwable) {
    false
  }

  private var tokenizer: StringTokenizer? = null
  private val reader = BufferedReader(InputStreamReader(stream), 32768)
  private fun next(): String {
    while (tokenizer == null || !tokenizer!!.hasMoreTokens()) {
      tokenizer = StringTokenizer(reader.readLine())
    }
    return tokenizer!!.nextToken()
  }

  private fun ni() = next().toInt()
  private fun nl() = next().toLong()
  private fun ns() = next()
  private fun na(n: Int, offset: Int = 0): IntArray {
    return map(n) { ni() + offset }
  }
  private fun nal(n: Int, offset: Int = 0): LongArray {
    val res = LongArray(n)
    for (i in 0 until n) {
      res[i] = nl() + offset
    }
    return res
  }

  private fun na2(n: Int, offset: Int = 0): Array<IntArray> {
    val a  = Array(2){IntArray(n)}
    for (i in 0 until n) {
      for (e in a) {
        e[i] = ni() + offset
      }
    }
    return a
  }

  private inline fun map(n: Int, f: (Int) -> Int): IntArray {
    val res = IntArray(n)
    for (i in 0 until n) {
      res[i] = f(i)
    }
    return res
  }

  private inline fun debug(msg: () -> String) {
    if (isDebug) System.err.println(msg())
  }

  private fun debug(a: LongArray) {
    debug { a.joinToString(" ") }
  }

  private fun debug(a: IntArray) {
    debug { a.joinToString(" ") }
  }

  private fun debug(a: BooleanArray) {
    debug { a.map { if (it) 1 else 0 }.joinToString("") }
  }

  private fun debugDim(A: Array<LongArray>) {
    if (isDebug) {
      for (a in A) {
        debug(a)
      }
    }
  }
  private fun debugDim(A: Array<IntArray>) {
    if (isDebug) {
      for (a in A) {
        debug(a)
      }
    }
  }

  /**
   * 勝手にimport消されるのを防ぎたい
   */
  private fun hoge() {
    min(1, 2)
    max(1, 2)
    abs(-10)
  }
}

fun main() {
  val out = java.io.PrintWriter(System.out)
  Solver(System.`in`, out).solve()
  out.flush()
}


import java.io.BufferedReader
import java.io.InputStream
import java.io.InputStreamReader
import java.util.*
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min

val MOD = 1_000_000_007L

data class Contestant(val v: Long, val cnt: Long)
class Solver(stream: InputStream, private val out: java.io.PrintWriter) {
  fun solve() {
    val N = ni()
    val S = nl()

    var sum = 0L
    val C = Array(N){
      val s = nl()
      val a = nl()
      val b = nl()
      sum += a * s
      Contestant(b - a, s)
    }

    val lst = C.map{it.cnt}.sum() % S
    debug{"sum:$sum all:${C.map{it.cnt}.sum()} lst:$lst"}

    C.sortBy { - it.v }
    debug{C.joinToString(" ")}

    var ans = sum
    var eaten = 0L
    var toGain = 0L
    for (i in C) {
      var cnt = i.cnt

      fun eatUntil(a: Long) {
        if (eaten < a  && cnt >= a - eaten) {
          val eat = a - eaten
          sum += toGain + eat * i.v
          ans = max(sum, ans)
          eaten = (eaten + eat) % S
          toGain = 0
          cnt -= eat
          debug{"eat($a) $toGain $eaten $sum"}
        }
      }

      eatUntil(lst)
      eatUntil(S)

      if (eaten == 0L) {
        val c = cnt / S
        sum += c * S * i.v
        ans = max(sum, ans)
        cnt -= c * S
      }

      if (cnt > 0) {
        eatUntil(lst)

        eaten += cnt
        toGain += cnt * i.v
      }

      debug{"$toGain $eaten $sum"}
    }
    out.println(ans)
  }

  private val isDebug = try {
    // なんか本番でエラーでる
    System.getenv("MY_DEBUG") != null
  } catch (t: Throwable) {
    false
  }

  private var tokenizer: StringTokenizer? = null
  private val reader = BufferedReader(InputStreamReader(stream), 32768)
  private fun next(): String {
    while (tokenizer == null || !tokenizer!!.hasMoreTokens()) {
      tokenizer = StringTokenizer(reader.readLine())
    }
    return tokenizer!!.nextToken()
  }

  private fun ni() = next().toInt()
  private fun nl() = next().toLong()
  private fun ns() = next()
  private fun na(n: Int, offset: Int = 0): IntArray {
    return map(n) { ni() + offset }
  }
  private fun nal(n: Int, offset: Int = 0): LongArray {
    val res = LongArray(n)
    for (i in 0 until n) {
      res[i] = nl() + offset
    }
    return res
  }

  private fun na2(n: Int, offset: Int = 0): Array<IntArray> {
    val a  = Array(2){IntArray(n)}
    for (i in 0 until n) {
      for (e in a) {
        e[i] = ni() + offset
      }
    }
    return a
  }

  private inline fun map(n: Int, f: (Int) -> Int): IntArray {
    val res = IntArray(n)
    for (i in 0 until n) {
      res[i] = f(i)
    }
    return res
  }

  private inline fun debug(msg: () -> String) {
    if (isDebug) System.err.println(msg())
  }

  private fun debug(a: LongArray) {
    debug { a.joinToString(" ") }
  }

  private fun debug(a: IntArray) {
    debug { a.joinToString(" ") }
  }

  private fun debug(a: BooleanArray) {
    debug { a.map { if (it) 1 else 0 }.joinToString("") }
  }

  private fun debugDim(A: Array<LongArray>) {
    if (isDebug) {
      for (a in A) {
        debug(a)
      }
    }
  }
  private fun debugDim(A: Array<IntArray>) {
    if (isDebug) {
      for (a in A) {
        debug(a)
      }
    }
  }

  /**
   * 勝手にimport消されるのを防ぎたい
   */
  private fun hoge() {
    min(1, 2)
    max(1, 2)
    abs(-10)
  }
}

fun main() {
  val out = java.io.PrintWriter(System.out)
  Solver(System.`in`, out).solve()
  out.flush()
}


import java.io.BufferedReader
import java.io.InputStream
import java.io.InputStreamReader
import java.util.*
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min

val MOD = 1_000_000_007L

data class Contestant(val v: Long, val cnt: Long)
class Solver(stream: InputStream, private val out: java.io.PrintWriter) {
  fun solve() {
    val N = ni()
    val S = nl()

    var sum = 0L
    val C = MutableList(N){
      val s = nl()
      val a = nl()
      val b = nl()
      sum += a * s
      Contestant(b - a, s)
    }

    val lst = C.map{it.cnt}.sum() % S
    debug{"sum:$sum all:${C.map{it.cnt}.sum()} lst:$lst"}

    if (lst > 0) C += Contestant(0, S - lst)

    C.sortBy { - it.v }
    debug{C.joinToString(" ")}

    var ans = sum
    var eaten = 0L
    var toGain = 0L
    for (i in C) {
      var cnt = i.cnt

      fun eatUntil(a: Long) {
        if (eaten < a  && cnt >= a - eaten) {
          val eat = a - eaten
          sum += toGain + eat * i.v
          ans = max(sum, ans)
          eaten = 0
          toGain = 0
          cnt -= eat
          debug{"eat($a) $toGain $eaten $sum"}
        }
      }

      eatUntil(S)

      if (eaten == 0L) {
        val c = cnt / S
        sum += c * S * i.v
        ans = max(sum, ans)
        cnt -= c * S
      }

      if (cnt > 0) {
        eaten += cnt
        toGain += cnt * i.v
      }

      debug{"$toGain $eaten $sum"}
    }
    out.println(ans)
  }

  private val isDebug = try {
    // なんか本番でエラーでる
    System.getenv("MY_DEBUG") != null
  } catch (t: Throwable) {
    false
  }

  private var tokenizer: StringTokenizer? = null
  private val reader = BufferedReader(InputStreamReader(stream), 32768)
  private fun next(): String {
    while (tokenizer == null || !tokenizer!!.hasMoreTokens()) {
      tokenizer = StringTokenizer(reader.readLine())
    }
    return tokenizer!!.nextToken()
  }

  private fun ni() = next().toInt()
  private fun nl() = next().toLong()
  private fun ns() = next()
  private fun na(n: Int, offset: Int = 0): IntArray {
    return map(n) { ni() + offset }
  }
  private fun nal(n: Int, offset: Int = 0): LongArray {
    val res = LongArray(n)
    for (i in 0 until n) {
      res[i] = nl() + offset
    }
    return res
  }

  private fun na2(n: Int, offset: Int = 0): Array<IntArray> {
    val a  = Array(2){IntArray(n)}
    for (i in 0 until n) {
      for (e in a) {
        e[i] = ni() + offset
      }
    }
    return a
  }

  private inline fun map(n: Int, f: (Int) -> Int): IntArray {
    val res = IntArray(n)
    for (i in 0 until n) {
      res[i] = f(i)
    }
    return res
  }

  private inline fun debug(msg: () -> String) {
    if (isDebug) System.err.println(msg())
  }

  private fun debug(a: LongArray) {
    debug { a.joinToString(" ") }
  }

  private fun debug(a: IntArray) {
    debug { a.joinToString(" ") }
  }

  private fun debug(a: BooleanArray) {
    debug { a.map { if (it) 1 else 0 }.joinToString("") }
  }

  private fun debugDim(A: Array<LongArray>) {
    if (isDebug) {
      for (a in A) {
        debug(a)
      }
    }
  }
  private fun debugDim(A: Array<IntArray>) {
    if (isDebug) {
      for (a in A) {
        debug(a)
      }
    }
  }

  /**
   * 勝手にimport消されるのを防ぎたい
   */
  private fun hoge() {
    min(1, 2)
    max(1, 2)
    abs(-10)
  }
}

fun main() {
  val out = java.io.PrintWriter(System.out)
  Solver(System.`in`, out).solve()
  out.flush()
}


#include <stdio.h>
#include <stdlib.h>

typedef struct{
  int slice, a, b;
}Person;

Person p[100001], Pa[100001], Pb[100001];

int mycmp(const void* a, const void* b){
  const Person* sa = (const Person*)a;
  const Person* sb = (const Person*)b;
  return (sa->a-sa->b)-(sb->a-sb->b);
}

int main(){
  int n, s;
  scanf("%d %d", &n, &s);
  long long piza=0, pizb=0, ans=0;
  int numa=0, numb=0;

  for (int i=0; i<n; i++){
    scanf("%d %d %d", &p[i].slice, &p[i].a, &p[i].b);
    int tmp=p[i].a-p[i].b;

    if (tmp>0) {
      ans+=p[i].slice*p[i].a;
      piza+=p[i].slice;
      Pa[numa++]=p[i];
    }
    else {
      ans+=p[i].slice*p[i].b;
      pizb+=p[i].slice;
      Pb[numb++]=p[i];
    }
  }
  qsort(Pa, numa, sizeof(Person), mycmp);
  qsort(Pb, numb, sizeof(Person), mycmp);

  if (piza%s ==0 && pizb%s==0) printf("%I64d\n", ans);
  else if ((piza%s+pizb%s>s) || piza%s==0 || pizb%s==0) printf("%I64d\n", ans);
  else {
    long long ansa=ans, ansb=ans;
    int i=0;
    piza%=s; pizb%=s;
    while(piza--){
      ansa-=Pa[i].a;
      ansa+=Pa[i].b;
      Pa[i].slice--;
      if (!Pa[i].slice) i++;
    }
    i=numb-1;
    while(pizb--){
      ansb-=Pb[i].b;
      ansb+=Pb[i].a;
      Pb[i].slice--;
      if (!Pb[i].slice) i--;
    }
    printf("%I64d\n", ansa>ansb?ansa:ansb);
  }
}


#include <stdio.h>
#include <stdlib.h>

typedef struct{
  int slice, a, b;
}Person;

Person p[100001], Pa[100001], Pb[100001];

int mycmp(const void* a, const void* b){
  const Person* sa = (const Person*)a;
  const Person* sb = (const Person*)b;
  return (sa->a-sa->b)-(sb->a-sb->b);
}

int main(){
  int n, s;
  scanf("%d %d", &n, &s);
  long long piza=0, pizb=0, ans=0;
  int numa=0, numb=0;

  for (int i=0; i<n; i++){
    scanf("%d %d %d", &p[i].slice, &p[i].a, &p[i].b);
    int tmp=p[i].a-p[i].b;

    if (tmp>0) {
      ans+=p[i].slice*p[i].a;
      piza+=p[i].slice;
      Pa[numa++]=p[i];
    }
    else {
      ans+=p[i].slice*p[i].b;
      pizb+=p[i].slice;
      Pb[numb++]=p[i];
    }
  }
 qsort(Pa, numa, sizeof(Person), mycmp);
  qsort(Pb, numb, sizeof(Person), mycmp);

  if (piza%s ==0 && pizb%s==0) printf("%I64d\n", ans);
  else if ((piza%s+pizb%s>s) || piza%s==0 || pizb%s==0) printf("%I64d\n", ans);
  else {
    long long ansa=ans, ansb=ans;
    int i=0;
    piza%=s; pizb%=s;
    while(piza--){
      ansa-=Pa[i].a;
      ansa+=Pa[i].b;
      Pa[i].slice--;
      if (!Pa[i].slice) i++;
    }
    i=0;
    while(pizb--){
      ansb-=Pb[i].b;
      ansb+=Pb[i].a;
      Pb[i].slice--;
      if (!Pb[i].slice) i++;
    }
    printf("%I64d\n", ansa>ansb?ansa:ansb);
  }
}


#include <stdio.h>
#include <stdlib.h>

typedef struct{
  int slice, a, b;
}Person;

Person p[100001], Pa[100001], Pb[100001];

int mycmp(const void* a, const void* b){
  const Person* sa = (const Person*)a;
  const Person* sb = (const Person*)b;
  return (sa->a-sa->b)-(sb->a-sb->b);
}

int main(){
  int n, s;
  scanf("%d %d", &n, &s);
  long long piza=0, pizb=0, ans=0;
  int numa=0, numb=0;

  for (int i=0; i<n; i++){
    scanf("%d %d %d", &p[i].slice, &p[i].a, &p[i].b);
    int tmp=p[i].a-p[i].b;

    if (tmp>0) {
      ans+=(double)p[i].slice*p[i].a;
      piza+=(double)p[i].slice;
      Pa[numa++]=p[i];
    }
    else {
      ans+=(double)p[i].slice*p[i].b;
      pizb+=(double)p[i].slice;
      Pb[numb++]=p[i];
    }
  }
  qsort(Pa, numa, sizeof(Person), mycmp);
  qsort(Pb, numb, sizeof(Person), mycmp);

  if (piza%s ==0 && pizb%s==0) printf("%I64d\n", ans);
  else if ((piza%s+pizb%s>s) || piza%s==0 || pizb%s==0) printf("%I64d\n", ans);
  else {
    long long ansa=ans, ansb=ans;
    int i=0;
    piza%=s; pizb%=s;
    while(piza--){
      ansa-=(double)Pa[i].a;
      ansa+=(double)Pa[i].b;
      Pa[i].slice--;
      if (!Pa[i].slice) i++;
    }
    i=0;
    while(pizb--){
      ansb-=(double)Pb[i].b;
      ansb+=(double)Pb[i].a;
      Pb[i].slice--;
      if (!Pb[i].slice) i++;
    }
    printf("%I64d\n", ansa>ansb?ansa:ansb);
  }
}


#include <stdio.h>
#include <stdlib.h>

typedef struct{
  int slice, a, b;
}Person;

int n, s;
Person p[100001], Pa[100001], Pb[100001];

int mycmp(const void* a, const void* b){
  const Person* sa = (const Person*)a;
  const Person* sb = (const Person*)b;
  if ((sa->a-sa->b)>0) return sa->a-sa->b;
  else return -(sa->a-sa->b);
}

int main(){
  scanf("%d %d", &n, &s);
  long long piza=0, pizb=0, ans=0;
  int numa=0, numb=0;

  for (int i=0; i<n; i++){
    scanf("%d %d %d", &p[i].slice, &p[i].a, &p[i].b);
    int tmp=p[i].a-p[i].b;

    if (tmp>0) {
      ans+=p[i].slice*p[i].a;
      piza+=p[i].slice;
      Pa[numa++]=p[i];
    }
    else {
      ans+=p[i].slice*p[i].b;
      pizb+=p[i].slice;
      Pb[numb++]=p[i];
    }
  }

    qsort(Pa, numa, sizeof(Person), mycmp);
  qsort(Pb, numb, sizeof(Person), mycmp);
  if (piza%s ==0 && pizb%s==0) printf("%I64d\n", ans);
  else if (piza%s+pizb%s>s) printf("%I64d\n", ans);
  else {
    long long ansa=ans, ansb=ans;
    int i=0;
    piza%=s; pizb%=s;
    while(piza--){
      ansa-=Pa[i].a;
      ansa+=Pa[i].b;
      Pa[i].slice--;
      if (!Pa[i].slice) i++;
    }
    i=0;
    while(pizb--){
      ansb-=Pb[i].b;
      ansb+=Pb[i].a;
      Pb[i].slice--;
      if (!Pb[i].slice) i++;
    }
    printf("%I64d\n", ansa>ansb?ansa:ansb);
  }
}


#include <stdio.h>
#include <stdlib.h>

typedef struct{
  int slice, a, b;
}Person;

Person p[100001], Pa[100001], Pb[100001];

int mycmp(const void* a, const void* b){
  const Person* sa = (const Person*)a;
  const Person* sb = (const Person*)b;
  return (sa->a-sa->b)-(sb->a-sb->b);
}

int main(){
  int n, s;
  scanf("%d %d", &n, &s);
  long long piza=0, pizb=0, ans=0;
  int numa=0, numb=0;

  for (int i=0; i<n; i++){
    scanf("%d %d %d", &p[i].slice, &p[i].a, &p[i].b);
    int tmp=p[i].a-p[i].b;

    if (tmp>0) {
      ans+=(double)p[i].slice*p[i].a;
      piza+=p[i].slice;
      Pa[numa++]=p[i];
    }
    else {
      ans+=(double)p[i].slice*p[i].b;
      pizb+=p[i].slice;
      Pb[numb++]=p[i];
    }
  }
  qsort(Pa, numa, sizeof(Person), mycmp);
  qsort(Pb, numb, sizeof(Person), mycmp);

  if (piza%s ==0 && pizb%s==0) printf("%I64d\n", ans);
  else if ((piza%s+pizb%s>s) || piza%s==0 || pizb%s==0) printf("%I64d\n", ans);
  else {
    long long ansa=ans, ansb=ans;
    int i=0;
    piza%=s; pizb%=s;
    while(piza--){
      ansa-=Pa[i].a;
      ansa+=Pa[i].b;
      Pa[i].slice--;
      if (!Pa[i].slice) i++;
    }
    i=numb-1;
    while(pizb--){
      ansb-=Pb[i].b;
      ansb+=Pb[i].a;
      Pb[i].slice--;
      if (!Pb[i].slice) i--;
    }
    printf("%I64d\n", ansa>ansb?ansa:ansb);
  }
}


#include <bits/stdc++.h>
using namespace std;

// =============================================================================
// BEGIN template
// =============================================================================
#ifdef ONLINE_JUDGE
  #define fastio        std::ios::sync_with_stdio(false)
  #define dbg(X)
  #define _             _
#else
  #include <unistd.h>
  #define fastio
  #define dbg(X)        cerr << ">>> (" << #X << ") = (" << X << ")\n"
  #define _             << " _ " <<
#endif
#define   ff            first
#define   ss            second
#define   pb            push_back
#define   eb            emplace_back
#define   em            emplace
#define   pq            priority_queue
#define   pqmin(X)      priority_queue<X,vector<X>,greater<X>>
#define   all(X)        (X).begin(),(X).end()
#define   sci1(X)       scanf("%d",&(X))
#define   sci2(X,Y)     scanf("%d%d",&(X),&(Y))
#define   sci3(X,Y,Z)   scanf("%d%d%d",&(X),&(Y),&(Z))
#define   scl(X)        scanf("%lld",&(X))
#define   scs(X)        scanf("%s",X)
#define   flush         fflush(stdout)
#define   lg(X)         (63 - __builtin_clzll(X))
#define   LG(X)         (lg(X)+((1<<lg(X)) < (X)))
#define    rp(i,L,R)    for (ll i = L, __R = R; i <= __R; i++)
#define   rpd(i,R,L)    for (ll i = R, __L = L; __L <= i; i--)

// dps
#define DP1(type,X)\
  static type dp[X];\
  static bool mark[X];\
  auto& ans = dp[i];\
  if (mark[i]) return ans;\
  mark[i] = true;
#define DP2(type,X,Y)\
  static type dp[X][Y];\
  static bool mark[X][Y];\
  auto& ans = dp[i][j];\
  if (mark[i][j]) return ans;\
  mark[i][j] = true;
#define DP3(type,X,Y,Z)\
  static type dp[X][Y][Z];\
  static bool mark[X][Y][Z];\
  auto& ans = dp[i][j][k];\
  if (mark[i][j][k]) return ans;\
  mark[i][j][k] = true;

// overloads
#define GET_SCI(_1,_2,_3,NAME,...) NAME
#define sci(...) GET_SCI(__VA_ARGS__,sci3,sci2,sci1)(__VA_ARGS__)
#define GET_DP(_1,_2,_3,_4,NAME,...) NAME
#define DP(...) GET_DP(__VA_ARGS__,DP3,DP2,DP1)(__VA_ARGS__)

// types
typedef   long long     ll;
typedef   pair<int,int> ii;
typedef   vector<int>   vi;
typedef   vector<bool>  vb;
typedef   set<int>      si;

// constants
const ll  oo =          0x3f3f3f3f3f3f3f3fll;
const int LGN =         25;
const int MOD =         1e9+7;
const int N =           3e5+5;
// =============================================================================
// END template
// =============================================================================

// input
int n,adj[N][26],dep[N];
vector<ii> T[N];
vi depset[N];

int sub[N],big[N];
void dfs1(int u = 1, int p = 0, int d = 1) {
  dep[u] = d;
  depset[d].pb(u);
  sub[u] = 1;
  for (ii e : T[u]) if (e.ff != p) {
    int v = e.ff;
    adj[u][e.ss] = v;
    dfs1(v,u,d+1);
    sub[u] += sub[v];
    if (sub[big[u]] < sub[v]) big[u] = v;
  }
}

int ans[N],depans[N];
vector<ii> undo;
int merge(int u, int v) {
  int ans = 0;
  rp(c,0,25) if (adj[v][c]) {
    if (adj[u][c]) ans += merge(adj[u][c],adj[v][c]);
    else {
      ans += sub[adj[v][c]];
      adj[u][c] = adj[v][c];
      undo.eb(u,c);
    }
  }
  return ans;
}
void dfs2(int u = 1) {
  if (!big[u]) { ans[u] = 1; return; }
  rp(c,0,25) if (adj[u][c]) dfs2(adj[u][c]);
  ans[u] = sub[big[u]];
  rp(c,0,25) if (adj[u][c] && adj[u][c] != big[u]) {
    ans[u] += merge(big[u],adj[u][c]);
  }
  depans[dep[u]] += ans[u];
  for (ii e : undo) adj[e.ff][e.ss] = 0;
  undo.clear();
}

int main() {
  fastio;
  sci(n);
  rp(i,2,n) {
    int u,v;
    sci(u,v);
    char c[2];
    scs(c);
    T[u].eb(v,c[0]-'a');
    T[v].eb(u,c[0]-'a');
  }
  dfs1();
  dfs2();
  int depsum = 0;
  int ans = oo, p;
  rp(d,1,n) {
    depsum += depset[d-1].size();
    int alt = depsum+depans[d];
    if (alt < ans) ans = alt, p = d;
  }
  printf("%d\n%d\n",ans,p);
  return 0;
}


#include <bits/stdc++.h>
using namespace std;

// =============================================================================
// BEGIN template
// =============================================================================
#ifdef ONLINE_JUDGE
  #define fastio        std::ios::sync_with_stdio(false)
  #define dbg(X)
  #define _             _
#else
  #include <unistd.h>
  #define fastio
  #define dbg(X)        cerr << ">>> (" << #X << ") = (" << X << ")\n"
  #define _             << " _ " <<
#endif
#define   ff            first
#define   ss            second
#define   pb            push_back
#define   eb            emplace_back
#define   em            emplace
#define   pq            priority_queue
#define   pqmin(X)      priority_queue<X,vector<X>,greater<X>>
#define   all(X)        (X).begin(),(X).end()
#define   sci1(X)       scanf("%d",&(X))
#define   sci2(X,Y)     scanf("%d%d",&(X),&(Y))
#define   sci3(X,Y,Z)   scanf("%d%d%d",&(X),&(Y),&(Z))
#define   scl(X)        scanf("%lld",&(X))
#define   scs(X)        scanf("%s",X)
#define   flush         fflush(stdout)
#define   lg(X)         (63 - __builtin_clzll(X))
#define   LG(X)         (lg(X)+((1<<lg(X)) < (X)))
#define    rp(i,L,R)    for (ll i = L, __R = R; i <= __R; i++)
#define   rpd(i,R,L)    for (ll i = R, __L = L; __L <= i; i--)

// dps
#define DP1(type,X)\
  static type dp[X];\
  static bool mark[X];\
  auto& ans = dp[i];\
  if (mark[i]) return ans;\
  mark[i] = true;
#define DP2(type,X,Y)\
  static type dp[X][Y];\
  static bool mark[X][Y];\
  auto& ans = dp[i][j];\
  if (mark[i][j]) return ans;\
  mark[i][j] = true;
#define DP3(type,X,Y,Z)\
  static type dp[X][Y][Z];\
  static bool mark[X][Y][Z];\
  auto& ans = dp[i][j][k];\
  if (mark[i][j][k]) return ans;\
  mark[i][j][k] = true;

// overloads
#define GET_SCI(_1,_2,_3,NAME,...) NAME
#define sci(...) GET_SCI(__VA_ARGS__,sci3,sci2,sci1)(__VA_ARGS__)
#define GET_DP(_1,_2,_3,_4,NAME,...) NAME
#define DP(...) GET_DP(__VA_ARGS__,DP3,DP2,DP1)(__VA_ARGS__)

// types
typedef   long long     ll;
typedef   pair<int,int> ii;
typedef   vector<int>   vi;
typedef   vector<bool>  vb;
typedef   set<int>      si;

// constants
const ll  oo =          0x3f3f3f3f3f3f3f3fll;
const int LGN =         25;
const int MOD =         1e9+7;
const int N =           3e5+5;
// =============================================================================
// END template
// =============================================================================

const int hashmod[] = {(int)1e9+7,(int)1e9+9};
const int hashlen = sizeof(hashmod)/sizeof(int);
struct Hash {
  static long long mul;
  static long long div[hashlen];
  static long long power(long long a, long long b, long long m) {
    long long ans = 1;
    while (b > 0) {
      if (b&1) ans = (ans*a)%m;
      b >>= 1;
      a = (a*a)%m;
    }
    return ans;
  }
  static void init() {
    static bool ok;
    if (ok) return;
    ok = true;
    for (int i = 0; i < hashlen; i++) {
      div[i] = power(mul,hashmod[i]-2,hashmod[i]);
    }
  }

  int h[hashlen];
  Hash(char s) {
    memset(h,0,sizeof h);
    operator+=(s);
  }
  Hash(const string& s = "") {
    memset(h,0,sizeof h);
    operator+=(s);
  }
  bool operator<(const Hash& o) const {
    for (int i = 0; i < hashlen; i++) if (h[i] != o.h[i]) return h[i] < o.h[i];
    return false;
  }
  bool operator==(const Hash& o) const {
    for (int i = 0; i < hashlen; i++) if (h[i] != o.h[i]) return false;
    return true;
  }
  bool operator!=(const Hash& o) const {
    for (int i = 0; i < hashlen; i++) if (h[i] != o.h[i]) return true;
    return false;
  }
  Hash& operator+=(char s) {
    int c = s+1;
    for (int i = 0; i < hashlen; i++) h[i] = (h[i]*mul+c)%hashmod[i];
    return *this;
  }
  Hash& operator+=(const string& s) {
    for (char c : s) operator+=(c);
    return *this;
  }
  Hash& operator+=(const Hash& o) {
    for (int i = 0; i < hashlen; i++) h[i] = (h[i]+o.h[i])%hashmod[i];
    return *this;
  }
  Hash& operator-=(char s) {
    int c = s+1;
    for (int i = 0; i < hashlen; i++) {
      h[i] = ((h[i]+hashmod[i]-c)*div[i])%hashmod[i];
    }
    return *this;
  }
  Hash& operator-=(const string& s) {
    for (int i = s.size()-1; 0 <= i; i--) operator-=(s[i]);
    return *this;
  }
  Hash& operator-=(const Hash& o) {
    for (int i = 0; i < hashlen; i++) {
      h[i] = (h[i]+hashmod[i]-o.h[i])%hashmod[i];
    }
    return *this;
  }
  Hash& operator*=(long long pwr) {
    for (int i = 0; i < hashlen; i++) {
      h[i] = (h[i]*power(mul,pwr,hashmod[i]))%hashmod[i];
    }
    return *this;
  }
  Hash& operator/=(long long pwr) {
    for (int i = 0; i < hashlen; i++) {
      h[i] = (h[i]*power(div[i],pwr,hashmod[i]))%hashmod[i];
    }
    return *this;
  }
  Hash operator+(char s) const {
    Hash ans = *this;
    ans += s;
    return ans;
  }
  Hash operator+(const string& s) const {
    Hash ans = *this;
    ans += s;
    return ans;
  }
  Hash operator+(const Hash& o) const {
    Hash ans = *this;
    ans += o;
    return ans;
  }
  Hash operator-(char s) const {
    Hash ans = *this;
    ans -= s;
    return ans;
  }
  Hash operator-(const string& s) const {
    Hash ans = *this;
    ans -= s;
    return ans;
  }
  Hash operator-(const Hash& o) const {
    Hash ans = *this;
    ans -= o;
    return ans;
  }
  Hash operator*(long long pwr) const {
    Hash ans = *this;
    ans *= pwr;
    return ans;
  }
  Hash operator/(long long pwr) const {
    Hash ans = *this;
    ans /= pwr;
    return ans;
  }
};
long long Hash::mul = 257;
long long Hash::div[hashlen];

// input
int n,x[N*2],y[N*2];
char c[N*2];
vi T[N];

// output
int depans[N];

Hash h[N];
int sub[N],big[N],dep[N],depcnt[N];
void dfs1(int u = 1, int p = 0, Hash h = 0, int d = 1) {
  ::h[u] = h;
  sub[u] = 1;
  dep[u] = d;
  depcnt[d]++;
  for (int i : T[u]) if (y[i] != p) {
    int v = y[i];
    dfs1(v,u,h+c[i],d+1);
    sub[u] += sub[v];
    if (sub[big[u]] < sub[v]) big[u] = v;
  }
}

set<Hash> s[N];
void add(set<Hash>& tmp, int u, int p, Hash h) {
  tmp.insert(h);
  for (int i : T[u]) if (y[i] != p) add(tmp,y[i],u,h+c[i]);
}
void dfs2(int u = 1, int p = 0) {
  for (int i : T[u]) if (y[i] != p) dfs2(y[i],u);
  if (big[u]) s[u].swap(s[big[u]]), s[u].insert(h[big[u]]);
  set<Hash> sml;
  for (int i : T[u]) if (y[i] != p && y[i] != big[u]) add(sml,y[i],u,h[big[u]]);
  int ans = s[u].size();
  for (auto& h : sml) if (!s[u].count(h)) ans++;
  depans[dep[u]] += ans;
  for (int i : T[u]) if (y[i] != p && y[i] != big[u]) add(s[u],y[i],u,h[y[i]]);
}

int main() {
  fastio;
  sci(n);
  int cnt = 0;
  rp(i,2,n) {
    int u,v;
    sci(u,v);
    char c[2];
    scs(c);
    x[++cnt] = u, y[cnt] = v, ::c[cnt] = c[0], T[u].pb(cnt);
    x[++cnt] = v, y[cnt] = u, ::c[cnt] = c[0], T[v].pb(cnt);
  }
  dfs1();
  dfs2();
  int depsum = 0, ans = oo, p;
  rp(d,1,n) {
    depsum += depcnt[d-1];
    int alt = depsum+depans[d];
    if (alt < ans) ans = alt, p = d;
  }
  printf("%d\n%d\n",ans,p);
  return 0;
}


#include <bits/stdc++.h>
using namespace std;

// =============================================================================
// BEGIN template
// =============================================================================
#ifdef ONLINE_JUDGE
  #define fastio        std::ios::sync_with_stdio(false)
  #define dbg(X)
  #define _             _
#else
  #include <unistd.h>
  #define fastio
  #define dbg(X)        cerr << ">>> (" << #X << ") = (" << X << ")\n"
  #define _             << " _ " <<
#endif
#define   ff            first
#define   ss            second
#define   pb            push_back
#define   eb            emplace_back
#define   em            emplace
#define   pq            priority_queue
#define   pqmin(X)      priority_queue<X,vector<X>,greater<X>>
#define   all(X)        (X).begin(),(X).end()
#define   sci1(X)       scanf("%d",&(X))
#define   sci2(X,Y)     scanf("%d%d",&(X),&(Y))
#define   sci3(X,Y,Z)   scanf("%d%d%d",&(X),&(Y),&(Z))
#define   scl(X)        scanf("%lld",&(X))
#define   scs(X)        scanf("%s",X)
#define   flush         fflush(stdout)
#define   lg(X)         (63 - __builtin_clzll(X))
#define   LG(X)         (lg(X)+((1<<lg(X)) < (X)))
#define    rp(i,L,R)    for (ll i = L, __R = R; i <= __R; i++)
#define   rpd(i,R,L)    for (ll i = R, __L = L; __L <= i; i--)

// dps
#define DP1(type,X)\
  static type dp[X];\
  static bool mark[X];\
  auto& ans = dp[i];\
  if (mark[i]) return ans;\
  mark[i] = true;
#define DP2(type,X,Y)\
  static type dp[X][Y];\
  static bool mark[X][Y];\
  auto& ans = dp[i][j];\
  if (mark[i][j]) return ans;\
  mark[i][j] = true;
#define DP3(type,X,Y,Z)\
  static type dp[X][Y][Z];\
  static bool mark[X][Y][Z];\
  auto& ans = dp[i][j][k];\
  if (mark[i][j][k]) return ans;\
  mark[i][j][k] = true;

// overloads
#define GET_SCI(_1,_2,_3,NAME,...) NAME
#define sci(...) GET_SCI(__VA_ARGS__,sci3,sci2,sci1)(__VA_ARGS__)
#define GET_DP(_1,_2,_3,_4,NAME,...) NAME
#define DP(...) GET_DP(__VA_ARGS__,DP3,DP2,DP1)(__VA_ARGS__)

// types
typedef   long long     ll;
typedef   pair<int,int> ii;
typedef   vector<int>   vi;
typedef   vector<bool>  vb;
typedef   set<int>      si;

// constants
const ll  oo =          0x3f3f3f3f3f3f3f3fll;
const int LGN =         25;
const int MOD =         1e9+7;
const int N =           3e5+5;
// =============================================================================
// END template
// =============================================================================

const int hashmod[] = {(int)1e9+7,(int)1e9+9};
const int hashlen = sizeof(hashmod)/sizeof(int);
struct Hash {
  static long long mul;
  static long long div[hashlen];
  static long long power(long long a, long long b, long long m) {
    long long ans = 1;
    while (b > 0) {
      if (b&1) ans = (ans*a)%m;
      b >>= 1;
      a = (a*a)%m;
    }
    return ans;
  }
  static void init() {
    static bool ok;
    if (ok) return;
    ok = true;
    for (int i = 0; i < hashlen; i++) {
      div[i] = power(mul,hashmod[i]-2,hashmod[i]);
    }
  }
  // non-static
  int h[hashlen];
  Hash(char s) {
    memset(h,0,sizeof h);
    operator+=(s);
  }
  Hash(const string& s = "") {
    memset(h,0,sizeof h);
    operator+=(s);
  }
  bool operator<(const Hash& o) const {
    for (int i = 0; i < hashlen; i++) if (h[i] != o.h[i]) return h[i] < o.h[i];
    return false;
  }
  bool operator==(const Hash& o) const {
    for (int i = 0; i < hashlen; i++) if (h[i] != o.h[i]) return false;
    return true;
  }
  bool operator!=(const Hash& o) const {
    for (int i = 0; i < hashlen; i++) if (h[i] != o.h[i]) return true;
    return false;
  }
  Hash& operator+=(char s) {
    int c = s+1;
    for (int i = 0; i < hashlen; i++) h[i] = (h[i]*mul+c)%hashmod[i];
    return *this;
  }
  Hash& operator+=(const string& s) {
    for (char c : s) operator+=(c);
    return *this;
  }
  Hash& operator+=(const Hash& o) {
    for (int i = 0; i < hashlen; i++) h[i] = (h[i]+o.h[i])%hashmod[i];
    return *this;
  }
  Hash& operator-=(char s) {
    int c = s+1;
    for (int i = 0; i < hashlen; i++) {
      h[i] = ((h[i]+hashmod[i]-c)*div[i])%hashmod[i];
    }
    return *this;
  }
  Hash& operator-=(const string& s) {
    for (int i = s.size()-1; 0 <= i; i--) operator-=(s[i]);
    return *this;
  }
  Hash& operator-=(const Hash& o) {
    for (int i = 0; i < hashlen; i++) {
      h[i] = (h[i]+hashmod[i]-o.h[i])%hashmod[i];
    }
    return *this;
  }
  Hash& operator*=(long long pwr) {
    for (int i = 0; i < hashlen; i++) {
      h[i] = (h[i]*power(mul,pwr,hashmod[i]))%hashmod[i];
    }
    return *this;
  }
  Hash& operator/=(long long pwr) {
    for (int i = 0; i < hashlen; i++) {
      h[i] = (h[i]*power(div[i],pwr,hashmod[i]))%hashmod[i];
    }
    return *this;
  }
  Hash operator+(char s) const {
    Hash ans = *this;
    ans += s;
    return ans;
  }
  Hash operator+(const string& s) const {
    Hash ans = *this;
    ans += s;
    return ans;
  }
  Hash operator+(const Hash& o) const {
    Hash ans = *this;
    ans += o;
    return ans;
  }
  Hash operator-(char s) const {
    Hash ans = *this;
    ans -= s;
    return ans;
  }
  Hash operator-(const string& s) const {
    Hash ans = *this;
    ans -= s;
    return ans;
  }
  Hash operator-(const Hash& o) const {
    Hash ans = *this;
    ans -= o;
    return ans;
  }
  Hash operator*(long long pwr) const {
    Hash ans = *this;
    ans *= pwr;
    return ans;
  }
  Hash operator/(long long pwr) const {
    Hash ans = *this;
    ans /= pwr;
    return ans;
  }
};
long long Hash::mul = 257;
long long Hash::div[hashlen];

// input
int n,x[N*2],y[N*2];
char c[N*2];
vi T[N];

// output
int depans[N];

Hash h[N];
int sub[N],big[N],dep[N],depcnt[N];
void dfs1(int u = 1, int p = 0, Hash h = 0, int d = 1) {
  ::h[u] = h;
  sub[u] = 1;
  dep[u] = d;
  depcnt[d]++;
  for (int i : T[u]) if (y[i] != p) {
    int v = y[i];
    dfs1(v,u,h+c[i],d+1);
    sub[u] += sub[v];
    if (sub[big[u]] < sub[v]) big[u] = v;
  }
}

set<Hash> s[N];
void add(set<Hash>& tmp, int u, int p, Hash h) {
  tmp.insert(h);
  for (int i : T[u]) if (y[i] != p) add(tmp,y[i],u,h+c[i]);
}
void dfs2(int u = 1, int p = 0) {
  if (!big[u]) { depans[dep[u]]++; return; }
  for (int i : T[u]) if (y[i] != p) dfs2(y[i],u);
  if (big[u]) s[u].swap(s[big[u]]), s[u].insert(h[big[u]]);
  set<Hash> sml;
  for (int i : T[u]) if (y[i] != p && y[i] != big[u]) add(sml,y[i],u,h[big[u]]);
  int ans = s[u].size();
  for (auto& h : sml) if (!s[u].count(h)) ans++;
  depans[dep[u]] += ans;
  for (int i : T[u]) if (y[i] != p && y[i] != big[u]) add(s[u],y[i],u,h[y[i]]);
}

int main() {
  fastio;
  sci(n);
  int cnt = 0;
  rp(i,2,n) {
    int u,v;
    sci(u,v);
    char c[2];
    scs(c);
    x[++cnt] = u, y[cnt] = v, ::c[cnt] = c[0], T[u].pb(cnt);
    x[++cnt] = v, y[cnt] = u, ::c[cnt] = c[0], T[v].pb(cnt);
  }
  dfs1();
  dfs2();
  int depsum = 0, ans = oo, p;
  rp(d,1,n) {
    depsum += depcnt[d-1];
    int alt = depsum+depans[d];
    if (alt < ans) ans = alt, p = d;
  }
  printf("%d\n%d\n",ans,p);
  return 0;
}


#include <bits/stdc++.h>
using namespace std;

// =============================================================================
// BEGIN template
// =============================================================================
#ifdef ONLINE_JUDGE
  #define fastio        std::ios::sync_with_stdio(false)
  #define dbg(X)
  #define _             _
#else
  #include <unistd.h>
  #define fastio
  #define dbg(X)        cerr << ">>> (" << #X << ") = (" << X << ")\n"
  #define _             << " _ " <<
#endif
#define   ff            first
#define   ss            second
#define   pb            push_back
#define   eb            emplace_back
#define   em            emplace
#define   pq            priority_queue
#define   pqmin(X)      priority_queue<X,vector<X>,greater<X>>
#define   all(X)        (X).begin(),(X).end()
#define   sci1(X)       scanf("%d",&(X))
#define   sci2(X,Y)     scanf("%d%d",&(X),&(Y))
#define   sci3(X,Y,Z)   scanf("%d%d%d",&(X),&(Y),&(Z))
#define   scl(X)        scanf("%lld",&(X))
#define   scs(X)        scanf("%s",X)
#define   flush         fflush(stdout)
#define   lg(X)         (63 - __builtin_clzll(X))
#define   LG(X)         (lg(X)+((1<<lg(X)) < (X)))
#define    rp(i,L,R)    for (ll i = L, __R = R; i <= __R; i++)
#define   rpd(i,R,L)    for (ll i = R, __L = L; __L <= i; i--)

// dps
#define DP1(type,X)\
  static type dp[X];\
  static bool mark[X];\
  auto& ans = dp[i];\
  if (mark[i]) return ans;\
  mark[i] = true;
#define DP2(type,X,Y)\
  static type dp[X][Y];\
  static bool mark[X][Y];\
  auto& ans = dp[i][j];\
  if (mark[i][j]) return ans;\
  mark[i][j] = true;
#define DP3(type,X,Y,Z)\
  static type dp[X][Y][Z];\
  static bool mark[X][Y][Z];\
  auto& ans = dp[i][j][k];\
  if (mark[i][j][k]) return ans;\
  mark[i][j][k] = true;

// overloads
#define GET_SCI(_1,_2,_3,NAME,...) NAME
#define sci(...) GET_SCI(__VA_ARGS__,sci3,sci2,sci1)(__VA_ARGS__)
#define GET_DP(_1,_2,_3,_4,NAME,...) NAME
#define DP(...) GET_DP(__VA_ARGS__,DP3,DP2,DP1)(__VA_ARGS__)

// types
typedef   long long     ll;
typedef   pair<int,int> ii;
typedef   vector<int>   vi;
typedef   vector<bool>  vb;
typedef   set<int>      si;

// constants
const ll  oo =          0x3f3f3f3f3f3f3f3fll;
const int LGN =         25;
const int MOD =         1e9+7;
const int N =           3e5+5;
// =============================================================================
// END template
// =============================================================================

const int hashmod[] = {(int)1e9+7,(int)1e9+9};
const int hashlen = sizeof(hashmod)/sizeof(int);
struct Hash {
  static long long mul;
  static long long div[hashlen];
  static long long power(long long a, long long b, long long m) {
    long long ans = 1;
    while (b > 0) {
      if (b&1) ans = (ans*a)%m;
      b >>= 1;
      a = (a*a)%m;
    }
    return ans;
  }
  static void init() {
    static bool ok;
    if (ok) return;
    ok = true;
    for (int i = 0; i < hashlen; i++) {
      div[i] = power(mul,hashmod[i]-2,hashmod[i]);
    }
  }

  int h[hashlen];
  Hash(char s) {
    memset(h,0,sizeof h);
    operator+=(s);
  }
  Hash(const string& s = "") {
    memset(h,0,sizeof h);
    operator+=(s);
  }
  bool operator<(const Hash& o) const {
    for (int i = 0; i < hashlen; i++) if (h[i] != o.h[i]) return h[i] < o.h[i];
    return false;
  }
  bool operator==(const Hash& o) const {
    for (int i = 0; i < hashlen; i++) if (h[i] != o.h[i]) return false;
    return true;
  }
  bool operator!=(const Hash& o) const {
    for (int i = 0; i < hashlen; i++) if (h[i] != o.h[i]) return true;
    return false;
  }
  Hash& operator+=(char s) {
    int c = s+1;
    for (int i = 0; i < hashlen; i++) h[i] = (h[i]*mul+c)%hashmod[i];
    return *this;
  }
  Hash& operator+=(const string& s) {
    for (char c : s) operator+=(c);
    return *this;
  }
  Hash& operator+=(const Hash& o) {
    for (int i = 0; i < hashlen; i++) h[i] = (h[i]+o.h[i])%hashmod[i];
    return *this;
  }
  Hash& operator-=(char s) {
    int c = s+1;
    for (int i = 0; i < hashlen; i++) {
      h[i] = ((h[i]+hashmod[i]-c)*div[i])%hashmod[i];
    }
    return *this;
  }
  Hash& operator-=(const string& s) {
    for (int i = s.size()-1; 0 <= i; i--) operator-=(s[i]);
    return *this;
  }
  Hash& operator-=(const Hash& o) {
    for (int i = 0; i < hashlen; i++) {
      h[i] = (h[i]+hashmod[i]-o.h[i])%hashmod[i];
    }
    return *this;
  }
  Hash& operator*=(long long pwr) {
    for (int i = 0; i < hashlen; i++) {
      h[i] = (h[i]*power(mul,pwr,hashmod[i]))%hashmod[i];
    }
    return *this;
  }
  Hash& operator/=(long long pwr) {
    for (int i = 0; i < hashlen; i++) {
      h[i] = (h[i]*power(div[i],pwr,hashmod[i]))%hashmod[i];
    }
    return *this;
  }
  Hash operator+(char s) const {
    Hash ans = *this;
    ans += s;
    return ans;
  }
  Hash operator+(const string& s) const {
    Hash ans = *this;
    ans += s;
    return ans;
  }
  Hash operator+(const Hash& o) const {
    Hash ans = *this;
    ans += o;
    return ans;
  }
  Hash operator-(char s) const {
    Hash ans = *this;
    ans -= s;
    return ans;
  }
  Hash operator-(const string& s) const {
    Hash ans = *this;
    ans -= s;
    return ans;
  }
  Hash operator-(const Hash& o) const {
    Hash ans = *this;
    ans -= o;
    return ans;
  }
  Hash operator*(long long pwr) const {
    Hash ans = *this;
    ans *= pwr;
    return ans;
  }
  Hash operator/(long long pwr) const {
    Hash ans = *this;
    ans /= pwr;
    return ans;
  }
};
long long Hash::mul = 257;
long long Hash::div[hashlen];

// input
int n,x[N*2],y[N*2];
char c[N*2];
vi T[N];

// output
int depans[N];

Hash h[N];
int sub[N],big[N],dep[N],depcnt[N];
void dfs1(int u = 1, int p = 0, Hash h = 0, int d = 1) {
  ::h[u] = h;
  sub[u] = 1;
  dep[u] = d;
  depcnt[d]++;
  for (int i : T[u]) if (y[i] != p) {
    int v = y[i];
    dfs1(v,u,h+c[i],d+1);
    sub[u] += sub[v];
    if (sub[big[u]] < sub[v]) big[u] = v;
  }
}

set<Hash> s[N];
void add(set<Hash>& tmp, int u, int p, Hash h) {
  tmp.insert(h);
  for (int i : T[u]) if (y[i] != p) add(tmp,y[i],u,h+c[i]);
}
void dfs2(int u = 1, int p = 0) {
  if (!big[u]) { depans[dep[u]]++; return; }
  for (int i : T[u]) if (y[i] != p) dfs2(y[i],u);
  if (big[u]) s[u].swap(s[big[u]]), s[u].insert(h[big[u]]);
  set<Hash> sml;
  for (int i : T[u]) if (y[i] != p && y[i] != big[u]) add(sml,y[i],u,h[big[u]]);
  int ans = s[u].size();
  for (auto& h : sml) if (!s[u].count(h)) ans++;
  depans[dep[u]] += ans;
  for (int i : T[u]) if (y[i] != p && y[i] != big[u]) add(s[u],y[i],u,h[y[i]]);
}

int main() {
  fastio;
  sci(n);
  int cnt = 0;
  rp(i,2,n) {
    int u,v;
    sci(u,v);
    char c[2];
    scs(c);
    x[++cnt] = u, y[cnt] = v, ::c[cnt] = c[0], T[u].pb(cnt);
    x[++cnt] = v, y[cnt] = u, ::c[cnt] = c[0], T[v].pb(cnt);
  }
  dfs1();
  dfs2();
  int depsum = 0, ans = oo, p;
  rp(d,1,n) {
    depsum += depcnt[d-1];
    int alt = depsum+depans[d];
    if (alt < ans) ans = alt, p = d;
  }
  printf("%d\n%d\n",ans,p);
  return 0;
}


#include <bits/stdc++.h>
using namespace std;

// =============================================================================
// BEGIN template
// =============================================================================
#ifdef ONLINE_JUDGE
  #define fastio        std::ios::sync_with_stdio(false)
  #define dbg(X)
  #define _             _
#else
  #include <unistd.h>
  #define fastio
  #define dbg(X)        cerr << ">>> (" << #X << ") = (" << X << ")\n"
  #define _             << " _ " <<
#endif
#define   ff            first
#define   ss            second
#define   pb            push_back
#define   eb            emplace_back
#define   em            emplace
#define   pq            priority_queue
#define   pqmin(X)      priority_queue<X,vector<X>,greater<X>>
#define   all(X)        (X).begin(),(X).end()
#define   sci1(X)       scanf("%d",&(X))
#define   sci2(X,Y)     scanf("%d%d",&(X),&(Y))
#define   sci3(X,Y,Z)   scanf("%d%d%d",&(X),&(Y),&(Z))
#define   scl(X)        scanf("%lld",&(X))
#define   scs(X)        scanf("%s",X)
#define   flush         fflush(stdout)
#define   lg(X)         (63 - __builtin_clzll(X))
#define   LG(X)         (lg(X)+((1<<lg(X)) < (X)))
#define    rp(i,L,R)    for (ll i = L, __R = R; i <= __R; i++)
#define   rpd(i,R,L)    for (ll i = R, __L = L; __L <= i; i--)

// dps
#define DP1(type,X)\
  static type dp[X];\
  static bool mark[X];\
  auto& ans = dp[i];\
  if (mark[i]) return ans;\
  mark[i] = true;
#define DP2(type,X,Y)\
  static type dp[X][Y];\
  static bool mark[X][Y];\
  auto& ans = dp[i][j];\
  if (mark[i][j]) return ans;\
  mark[i][j] = true;
#define DP3(type,X,Y,Z)\
  static type dp[X][Y][Z];\
  static bool mark[X][Y][Z];\
  auto& ans = dp[i][j][k];\
  if (mark[i][j][k]) return ans;\
  mark[i][j][k] = true;

// overloads
#define GET_SCI(_1,_2,_3,NAME,...) NAME
#define sci(...) GET_SCI(__VA_ARGS__,sci3,sci2,sci1)(__VA_ARGS__)
#define GET_DP(_1,_2,_3,_4,NAME,...) NAME
#define DP(...) GET_DP(__VA_ARGS__,DP3,DP2,DP1)(__VA_ARGS__)

// types
typedef   long long     ll;
typedef   pair<int,int> ii;
typedef   vector<int>   vi;
typedef   vector<bool>  vb;
typedef   set<int>      si;

// constants
const ll  oo =          0x3f3f3f3f3f3f3f3fll;
const int LGN =         25;
const int MOD =         1e9+7;
const int N =           3e5+5;
// =============================================================================
// END template
// =============================================================================

const int hashmod[] = {(int)1e9+7,(int)1e9+9};
const int hashlen = sizeof(hashmod)/sizeof(int);
struct Hash {
  static long long mul;
  static long long div[hashlen];
  static long long power(long long a, long long b, long long m) {
    long long ans = 1;
    while (b > 0) {
      if (b&1) ans = (ans*a)%m;
      b >>= 1;
      a = (a*a)%m;
    }
    return ans;
  }
  static void init() {
    static bool ok;
    if (ok) return;
    ok = true;
    for (int i = 0; i < hashlen; i++) {
      div[i] = power(mul,hashmod[i]-2,hashmod[i]);
    }
  }
  // non-static
  int h[hashlen];
  Hash(char s) {
    memset(h,0,sizeof h);
    operator+=(s);
  }
  Hash(const string& s = "") {
    memset(h,0,sizeof h);
    operator+=(s);
  }
  bool operator<(const Hash& o) const {
    for (int i = 0; i < hashlen; i++) if (h[i] != o.h[i]) return h[i] < o.h[i];
    return false;
  }
  bool operator==(const Hash& o) const {
    for (int i = 0; i < hashlen; i++) if (h[i] != o.h[i]) return false;
    return true;
  }
  bool operator!=(const Hash& o) const {
    for (int i = 0; i < hashlen; i++) if (h[i] != o.h[i]) return true;
    return false;
  }
  Hash& operator+=(char s) {
    int c = s+1;
    for (int i = 0; i < hashlen; i++) h[i] = (h[i]*mul+c)%hashmod[i];
    return *this;
  }
  Hash& operator+=(const string& s) {
    for (char c : s) operator+=(c);
    return *this;
  }
  Hash& operator+=(const Hash& o) {
    for (int i = 0; i < hashlen; i++) h[i] = (h[i]+o.h[i])%hashmod[i];
    return *this;
  }
  Hash& operator-=(char s) {
    int c = s+1;
    for (int i = 0; i < hashlen; i++) {
      h[i] = ((h[i]+hashmod[i]-c)*div[i])%hashmod[i];
    }
    return *this;
  }
  Hash& operator-=(const string& s) {
    for (int i = s.size()-1; 0 <= i; i--) operator-=(s[i]);
    return *this;
  }
  Hash& operator-=(const Hash& o) {
    for (int i = 0; i < hashlen; i++) {
      h[i] = (h[i]+hashmod[i]-o.h[i])%hashmod[i];
    }
    return *this;
  }
  Hash& operator*=(long long pwr) {
    for (int i = 0; i < hashlen; i++) {
      h[i] = (h[i]*power(mul,pwr,hashmod[i]))%hashmod[i];
    }
    return *this;
  }
  Hash& operator/=(long long pwr) {
    for (int i = 0; i < hashlen; i++) {
      h[i] = (h[i]*power(div[i],pwr,hashmod[i]))%hashmod[i];
    }
    return *this;
  }
  Hash operator+(char s) const {
    Hash ans = *this;
    ans += s;
    return ans;
  }
  Hash operator+(const string& s) const {
    Hash ans = *this;
    ans += s;
    return ans;
  }
  Hash operator+(const Hash& o) const {
    Hash ans = *this;
    ans += o;
    return ans;
  }
  Hash operator-(char s) const {
    Hash ans = *this;
    ans -= s;
    return ans;
  }
  Hash operator-(const string& s) const {
    Hash ans = *this;
    ans -= s;
    return ans;
  }
  Hash operator-(const Hash& o) const {
    Hash ans = *this;
    ans -= o;
    return ans;
  }
  Hash operator*(long long pwr) const {
    Hash ans = *this;
    ans *= pwr;
    return ans;
  }
  Hash operator/(long long pwr) const {
    Hash ans = *this;
    ans /= pwr;
    return ans;
  }
};
long long Hash::mul = 257;
long long Hash::div[hashlen];

// input
int n,x[N*2],y[N*2];
char c[N*2];
vi T[N];

// output
int depans[N];

Hash h[N];
int sub[N],big[N],dep[N],depcnt[N];
void dfs1(int u = 1, int p = 0, Hash h = 0, int d = 1) {
  ::h[u] = h;
  sub[u] = 1;
  dep[u] = d;
  depcnt[d]++;
  for (int i : T[u]) if (y[i] != p) {
    int v = y[i];
    dfs1(v,u,h+c[i],d+1);
    sub[u] += sub[v];
    if (sub[big[u]] < sub[v]) big[u] = v;
  }
}

set<Hash> s[N];
void add(set<Hash>& tmp, int u, int p, Hash h) {
  tmp.insert(h);
  for (int i : T[u]) if (y[i] != p) add(tmp,y[i],u,h+c[i]);
}
void dfs2(int u = 1, int p = 0, bool keep = false) {
  if (!big[u]) { depans[dep[u]]++; return; }
  for (int i : T[u]) if (y[i] != p && y[i] != big[u]) dfs2(y[i],u);
  dfs2(big[u],u,true), s[u].swap(s[big[u]]), s[u].insert(h[big[u]]);
  set<Hash> sml;
  for (int i : T[u]) if (y[i] != p && y[i] != big[u]) add(sml,y[i],u,h[big[u]]);
  int ans = s[u].size();
  for (auto& h : sml) if (!s[u].count(h)) ans++;
  depans[dep[u]] += ans;
  for (int i : T[u]) if (y[i] != p && y[i] != big[u]) add(s[u],y[i],u,h[y[i]]);
  if (!keep) s[u].clear();
}

int main() {
  fastio;
  sci(n);
  int cnt = 0;
  rp(i,2,n) {
    int u,v;
    sci(u,v);
    char c[2];
    scs(c);
    x[++cnt] = u, y[cnt] = v, ::c[cnt] = c[0], T[u].pb(cnt);
    x[++cnt] = v, y[cnt] = u, ::c[cnt] = c[0], T[v].pb(cnt);
  }
  dfs1();
  dfs2();
  int depsum = 0, ans = oo, p;
  rp(d,1,n) {
    depsum += depcnt[d-1];
    int alt = depsum+depans[d];
    if (alt < ans) ans = alt, p = d;
  }
  printf("%d\n%d\n",ans,p);
  return 0;
}


#include <bits/stdc++.h>
using namespace std;

// =============================================================================
// BEGIN template
// =============================================================================
#ifdef ONLINE_JUDGE
  #define fastio        std::ios::sync_with_stdio(false)
  #define dbg(X)
  #define _             _
#else
  #include <unistd.h>
  #define fastio
  #define dbg(X)        cerr << ">>> (" << #X << ") = (" << X << ")\n"
  #define _             << " _ " <<
#endif
#define   ff            first
#define   ss            second
#define   pb            push_back
#define   eb            emplace_back
#define   em            emplace
#define   pq            priority_queue
#define   pqmin(X)      priority_queue<X,vector<X>,greater<X>>
#define   all(X)        (X).begin(),(X).end()
#define   sci1(X)       scanf("%d",&(X))
#define   sci2(X,Y)     scanf("%d%d",&(X),&(Y))
#define   sci3(X,Y,Z)   scanf("%d%d%d",&(X),&(Y),&(Z))
#define   scl(X)        scanf("%lld",&(X))
#define   scs(X)        scanf("%s",X)
#define   flush         fflush(stdout)
#define   lg(X)         (63 - __builtin_clzll(X))
#define   LG(X)         (lg(X)+((1<<lg(X)) < (X)))
#define    rp(i,L,R)    for (ll i = L, __R = R; i <= __R; i++)
#define   rpd(i,R,L)    for (ll i = R, __L = L; __L <= i; i--)

// dps
#define DP1(type,X)\
  static type dp[X];\
  static bool mark[X];\
  auto& ans = dp[i];\
  if (mark[i]) return ans;\
  mark[i] = true;
#define DP2(type,X,Y)\
  static type dp[X][Y];\
  static bool mark[X][Y];\
  auto& ans = dp[i][j];\
  if (mark[i][j]) return ans;\
  mark[i][j] = true;
#define DP3(type,X,Y,Z)\
  static type dp[X][Y][Z];\
  static bool mark[X][Y][Z];\
  auto& ans = dp[i][j][k];\
  if (mark[i][j][k]) return ans;\
  mark[i][j][k] = true;

// overloads
#define GET_SCI(_1,_2,_3,NAME,...) NAME
#define sci(...) GET_SCI(__VA_ARGS__,sci3,sci2,sci1)(__VA_ARGS__)
#define GET_DP(_1,_2,_3,_4,NAME,...) NAME
#define DP(...) GET_DP(__VA_ARGS__,DP3,DP2,DP1)(__VA_ARGS__)

// types
typedef   long long     ll;
typedef   pair<int,int> ii;
typedef   vector<int>   vi;
typedef   vector<bool>  vb;
typedef   set<int>      si;

// constants
const ll  oo =          0x3f3f3f3f3f3f3f3fll;
const int LGN =         25;
const int MOD =         1e9+7;
const int N =           3e5+5;
// =============================================================================
// END template
// =============================================================================

// input
int n,adj[N][26],dep[N];
vector<ii> T[N];
vi depset[N];

int sub[N],big[N];
void dfs1(int u = 1, int p = 0, int d = 1) {
  dep[u] = d;
  depset[d].pb(u);
  sub[u] = 1;
  for (ii e : T[u]) if (e.ff != p) {
    int v = e.ff;
    adj[u][e.ss] = v;
    dfs1(v,u,d+1);
    sub[u] += sub[v];
    if (sub[big[u]] < sub[v]) big[u] = v;
  }
}

int ans[N],depans[N];
ii undo[N]; int undocnt;
int merge(int u, int v) {
  int ans = 0;
  rp(c,0,25) if (adj[v][c]) {
    if (adj[u][c]) ans += merge(adj[u][c],adj[v][c]);
    else {
      ans += sub[adj[v][c]];
      adj[u][c] = adj[v][c];
      undo[undocnt++] = ii(u,c);
    }
  }
  return ans;
}
void dfs2(int u = 1) {
  if (!big[u]) { ans[u] = 1; depans[dep[u]]++; return; }
  rp(c,0,25) if (adj[u][c]) dfs2(adj[u][c]);
  ans[u] = sub[big[u]];
  rp(c,0,25) if (adj[u][c] && adj[u][c] != big[u]) {
    ans[u] += merge(big[u],adj[u][c]);
  }
  while (undocnt) { ii e = undo[--undocnt]; adj[e.ff][e.ss] = 0; }
  depans[dep[u]] += ans[u];
}

int main() {
  fastio;
  sci(n);
  rp(i,2,n) {
    int u,v;
    sci(u,v);
    char c[2];
    scs(c);
    T[u].eb(v,c[0]-'a');
    T[v].eb(u,c[0]-'a');
  }
  dfs1();
  dfs2();
  int depsum = 0;
  int ans = oo, p;
  rp(d,1,n) {
    depsum += depset[d-1].size();
    int alt = depsum+depans[d];
    if (alt < ans) ans = alt, p = d;
  }
  printf("%d\n%d\n",ans,p);
  return 0;
}


#include <bits/stdc++.h>
using namespace std;

// =============================================================================
// BEGIN template
// =============================================================================
#ifdef ONLINE_JUDGE
  #define fastio        std::ios::sync_with_stdio(false)
  #define dbg(X)
  #define _             _
#else
  #include <unistd.h>
  #define fastio
  #define dbg(X)        cerr << ">>> (" << #X << ") = (" << X << ")\n"
  #define _             << " _ " <<
#endif
#define   ff            first
#define   ss            second
#define   pb            push_back
#define   eb            emplace_back
#define   em            emplace
#define   pq            priority_queue
#define   pqmin(X)      priority_queue<X,vector<X>,greater<X>>
#define   all(X)        (X).begin(),(X).end()
#define   sci1(X)       scanf("%d",&(X))
#define   sci2(X,Y)     scanf("%d%d",&(X),&(Y))
#define   sci3(X,Y,Z)   scanf("%d%d%d",&(X),&(Y),&(Z))
#define   scl(X)        scanf("%lld",&(X))
#define   scs(X)        scanf("%s",X)
#define   flush         fflush(stdout)
#define   lg(X)         (63 - __builtin_clzll(X))
#define   LG(X)         (lg(X)+((1<<lg(X)) < (X)))
#define    rp(i,L,R)    for (ll i = L, __R = R; i <= __R; i++)
#define   rpd(i,R,L)    for (ll i = R, __L = L; __L <= i; i--)

// dps
#define DP1(type,X)\
  static type dp[X];\
  static bool mark[X];\
  auto& ans = dp[i];\
  if (mark[i]) return ans;\
  mark[i] = true;
#define DP2(type,X,Y)\
  static type dp[X][Y];\
  static bool mark[X][Y];\
  auto& ans = dp[i][j];\
  if (mark[i][j]) return ans;\
  mark[i][j] = true;
#define DP3(type,X,Y,Z)\
  static type dp[X][Y][Z];\
  static bool mark[X][Y][Z];\
  auto& ans = dp[i][j][k];\
  if (mark[i][j][k]) return ans;\
  mark[i][j][k] = true;

// overloads
#define GET_SCI(_1,_2,_3,NAME,...) NAME
#define sci(...) GET_SCI(__VA_ARGS__,sci3,sci2,sci1)(__VA_ARGS__)
#define GET_DP(_1,_2,_3,_4,NAME,...) NAME
#define DP(...) GET_DP(__VA_ARGS__,DP3,DP2,DP1)(__VA_ARGS__)

// types
typedef   long long     ll;
typedef   pair<int,int> ii;
typedef   vector<int>   vi;
typedef   vector<bool>  vb;
typedef   set<int>      si;

// constants
const ll  oo =          0x3f3f3f3f3f3f3f3fll;
const int LGN =         25;
const int MOD =         1e9+7;
const int N =           3e5+5;
// =============================================================================
// END template
// =============================================================================

const int hashmod[] = {(int)1e9+7};
const int hashlen = sizeof(hashmod)/sizeof(int);
struct Hash {
  static long long mul;
  static long long div[hashlen];
  static long long power(long long a, long long b, long long m) {
    long long ans = 1;
    while (b > 0) {
      if (b&1) ans = (ans*a)%m;
      b >>= 1;
      a = (a*a)%m;
    }
    return ans;
  }
  static void init() {
    static bool ok;
    if (ok) return;
    ok = true;
    for (int i = 0; i < hashlen; i++) {
      div[i] = power(mul,hashmod[i]-2,hashmod[i]);
    }
  }

  int h[hashlen];
  Hash(char s) {
    memset(h,0,sizeof h);
    operator+=(s);
  }
  Hash(const string& s = "") {
    memset(h,0,sizeof h);
    operator+=(s);
  }
  bool operator<(const Hash& o) const {
    for (int i = 0; i < hashlen; i++) if (h[i] != o.h[i]) return h[i] < o.h[i];
    return false;
  }
  bool operator==(const Hash& o) const {
    for (int i = 0; i < hashlen; i++) if (h[i] != o.h[i]) return false;
    return true;
  }
  bool operator!=(const Hash& o) const {
    for (int i = 0; i < hashlen; i++) if (h[i] != o.h[i]) return true;
    return false;
  }
  Hash& operator+=(char s) {
    int c = s+1;
    for (int i = 0; i < hashlen; i++) h[i] = (h[i]*mul+c)%hashmod[i];
    return *this;
  }
  Hash& operator+=(const string& s) {
    for (char c : s) operator+=(c);
    return *this;
  }
  Hash& operator+=(const Hash& o) {
    for (int i = 0; i < hashlen; i++) h[i] = (h[i]+o.h[i])%hashmod[i];
    return *this;
  }
  Hash& operator-=(char s) {
    int c = s+1;
    for (int i = 0; i < hashlen; i++) {
      h[i] = ((h[i]+hashmod[i]-c)*div[i])%hashmod[i];
    }
    return *this;
  }
  Hash& operator-=(const string& s) {
    for (int i = s.size()-1; 0 <= i; i--) operator-=(s[i]);
    return *this;
  }
  Hash& operator-=(const Hash& o) {
    for (int i = 0; i < hashlen; i++) {
      h[i] = (h[i]+hashmod[i]-o.h[i])%hashmod[i];
    }
    return *this;
  }
  Hash& operator*=(long long pwr) {
    for (int i = 0; i < hashlen; i++) {
      h[i] = (h[i]*power(mul,pwr,hashmod[i]))%hashmod[i];
    }
    return *this;
  }
  Hash& operator/=(long long pwr) {
    for (int i = 0; i < hashlen; i++) {
      h[i] = (h[i]*power(div[i],pwr,hashmod[i]))%hashmod[i];
    }
    return *this;
  }
  Hash operator+(char s) const {
    Hash ans = *this;
    ans += s;
    return ans;
  }
  Hash operator+(const string& s) const {
    Hash ans = *this;
    ans += s;
    return ans;
  }
  Hash operator+(const Hash& o) const {
    Hash ans = *this;
    ans += o;
    return ans;
  }
  Hash operator-(char s) const {
    Hash ans = *this;
    ans -= s;
    return ans;
  }
  Hash operator-(const string& s) const {
    Hash ans = *this;
    ans -= s;
    return ans;
  }
  Hash operator-(const Hash& o) const {
    Hash ans = *this;
    ans -= o;
    return ans;
  }
  Hash operator*(long long pwr) const {
    Hash ans = *this;
    ans *= pwr;
    return ans;
  }
  Hash operator/(long long pwr) const {
    Hash ans = *this;
    ans /= pwr;
    return ans;
  }
};
long long Hash::mul = 257;
long long Hash::div[hashlen];

// input
int n,x[N*2],y[N*2];
char c[N*2];
vi T[N];

// output
int depans[N];

Hash h[N];
int sub[N],big[N],dep[N],depcnt[N];
void dfs1(int u = 1, int p = 0, Hash h = 0, int d = 1) {
  ::h[u] = h;
  sub[u] = 1;
  dep[u] = d;
  depcnt[d]++;
  for (int i : T[u]) if (y[i] != p) {
    int v = y[i];
    dfs1(v,u,h+c[i],d+1);
    sub[u] += sub[v];
    if (sub[big[u]] < sub[v]) big[u] = v;
  }
}

set<Hash> s[N];
void add(set<Hash>& tmp, int u, int p, Hash h) {
  tmp.insert(h);
  for (int i : T[u]) if (y[i] != p) add(tmp,y[i],u,h+c[i]);
}
void dfs2(int u = 1, int p = 0) {
  for (int i : T[u]) if (y[i] != p) dfs2(y[i],u);
  if (big[u]) s[u].swap(s[big[u]]), s[u].insert(h[big[u]]);
  set<Hash> sml;
  for (int i : T[u]) if (y[i] != p && y[i] != big[u]) add(sml,y[i],u,h[big[u]]);
  int ans = s[u].size();
  for (auto& h : sml) if (!s[u].count(h)) ans++;
  depans[dep[u]] += ans;
  for (int i : T[u]) if (y[i] != p && y[i] != big[u]) add(s[u],y[i],u,h[y[i]]);
}

int main() {
  fastio;
  sci(n);
  int cnt = 0;
  rp(i,2,n) {
    int u,v;
    sci(u,v);
    char c[2];
    scs(c);
    x[++cnt] = u, y[cnt] = v, ::c[cnt] = c[0], T[u].pb(cnt);
    x[++cnt] = v, y[cnt] = u, ::c[cnt] = c[0], T[v].pb(cnt);
  }
  dfs1();
  dfs2();
  int depsum = 0, ans = oo, p;
  rp(d,1,n) {
    depsum += depcnt[d-1];
    int alt = depsum+depans[d];
    if (alt < ans) ans = alt, p = d;
  }
  printf("%d\n%d\n",ans,p);
  return 0;
}


#include <bits/stdc++.h>
#define MAXN 300010
using namespace std;

typedef long long LL;

int ch[MAXN][26];
int ans[MAXN],maxdep = 0;
int N;

int merge(vector<int>now)
{
    int res = now.size()-1;
    vector<int>next;
    for (int i=0;i<26;i++)
    {
        next.clear();
        for (int j=0;j<now.size();j++)
            if (ch[now[j]][i]) next.push_back(ch[now[j]][i]);
        if (next.size()<=1) continue;
        res+=merge(next);
    }
    return res;
}

void dfs(int x,int dep)
{
    vector<int>now;
    now.clear();
    maxdep = max(maxdep,dep);
    for (int i=0;i<26;i++)
    {
        if (!ch[x][i]) continue;
        now.push_back(ch[x][i]);
    }
    //printf("Merge %d\n",x);
    int res = merge(now)+1;
    ans[dep]+=res;
    //printf("%d %d\n",x,res);
    for (int i=0;i<26;i++)
    {
        if (ch[x][i]) dfs(ch[x][i],dep+1);
    }
}

int main()
{
    scanf("%d",&N);
    for (int i=1;i<N;i++)
    {
        int u,v;
        char s[5];
        scanf("%d%d",&u,&v);
        scanf("%s",s);
        ch[u][s[0]-'a'] = v;
    }
    memset(ans,0,sizeof(ans));
    dfs(1,1);
    int maxres = 0,mp;
    for (int i=1;i<=maxdep;i++)
        if (maxres<ans[i])
        {
            maxres = ans[i];
            mp = i;
        }
    printf("%d\n%d\n",N-maxres,mp);
}


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=300005;
int n,u,v,x,trie[maxn<<1][26],sz[maxn<<1],tot,cnt[maxn],res;
char op[2];
int merge(int a,int b) {
    if (!a||!b)
        return a+b;
    int rt=++tot;
    memset(trie[rt],0,sizeof trie[rt]);
    sz[rt]=1;
    for (int i=0;i<26;++i) {
        trie[rt][i]=merge(trie[a][i],trie[b][i]);
        sz[rt]+=sz[trie[rt][i]];
    }
    return rt;
}
void dfs(int u,int dep) {
    sz[u]=1;
    for (int i=0;i<26;++i)
        if (trie[u][i]) {
            dfs(trie[u][i],dep+1);
            sz[u]+=sz[trie[u][i]];
        }
    cnt[dep]+=sz[u];
    tot=n;
    int rt=0;
    for (int i=0;i<26;++i)
        rt=merge(rt,trie[u][i]);
    cnt[dep]-=sz[rt];
}
int main()
{
    scanf("%d",&n);
    for (int i=0;i<n-1;++i) {
        scanf("%d%d%s",&u,&v,op);
        trie[u][op[0]-'a']=v;
    }
    sz[0]=1;
    dfs(1,0);
    res=0;
    for (int i=1;i<n;++i)
        if (cnt[i]>cnt[res])
            res=i;
    printf("%d\n%d\n",sz[1]-cnt[res],res+1);
    return 0;
}


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=300005;
int n,u,v,x,trie[maxn<<1][26],sz[maxn<<1],tot,cnt[maxn],res;
char op[2];
int merge(int a,int b) {
    if (!a||!b)
        return a+b;
    int rt=++tot;
    //memset(trie[rt],0,sizeof trie[rt]);
    sz[rt]=1;
    for (int i=0;i<26;++i) {
        trie[rt][i]=merge(trie[a][i],trie[b][i]);
        sz[rt]+=sz[trie[rt][i]];
    }
    return rt;
}
void dfs(int u,int dep) {
    sz[u]=1;
    for (int i=0;i<26;++i)
        if (trie[u][i]) {
            dfs(trie[u][i],dep+1);
            sz[u]+=sz[trie[u][i]];
        }
    cnt[dep]+=sz[u];
    tot=n;
    int rt=0;
    for (int i=0;i<26;++i)
        rt=merge(rt,trie[u][i]);
    cnt[dep]-=max(1,sz[rt]);
}
int main()
{
    scanf("%d",&n);
    for (int i=0;i<n-1;++i) {
        scanf("%d%d%s",&u,&v,op);
        trie[u][op[0]-'a']=v;
    }
    dfs(1,0);
    res=0;
    for (int i=1;i<n;++i)
        if (cnt[i]>cnt[res])
            res=i;
    printf("%d\n%d\n",sz[1]-cnt[res],res+1);
    return 0;
}


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=300005;
int n,u,v,x,trie[maxn<<1][26],sz[maxn<<1],tot,cnt[maxn],res;
char op[2];
int merge(int a,int b) {
    if (!a||!b)
        return a+b;
    int rt=++tot;
    memset(trie[rt],0,sizeof trie[rt]);
    sz[rt]=1;
    for (int i=0;i<26;++i) {
        trie[rt][i]=merge(trie[a][i],trie[b][i]);
        sz[rt]+=sz[trie[rt][i]];
    }
    return rt;
}
void dfs(int u,int dep) {
    sz[u]=1;
    for (int i=0;i<26;++i)
        if (trie[u][i]) {
            dfs(trie[u][i],dep+1);
            sz[u]+=sz[trie[u][i]];
        }
    cnt[dep]+=sz[u];
    tot=n;
    int rt=0;
    for (int i=0;i<26;++i)
        rt=merge(rt,trie[u][i]);
    cnt[dep]-=sz[rt];
}
int main()
{
    scanf("%d",&n);
    for (int i=0;i<n-1;++i) {
        scanf("%d%d%s",&u,&v,op);
        trie[u][op[0]-'a']=v;
    }
    dfs(1,0);
    res=0;
    for (int i=1;i<n;++i)
        if (cnt[i]>cnt[res])
            res=i;
    printf("%d\n%d\n",sz[1]-cnt[res],res+1);
    return 0;
}


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=300005;
int n,u,v,x,trie[maxn<<1][26],sz[maxn<<1],tot,cnt[maxn],res;
char op[2];
int merge(int a,int b) {
    if (!a||!b)
        return a+b;
    int rt=++tot;
    sz[rt]=1;
    for (int i=0;i<26;++i) {
        trie[rt][i]=merge(trie[a][i],trie[b][i]);
        if (trie[rt][i])
            sz[rt]+=sz[trie[rt][i]];
    }
    return rt;
}
void dfs(int u,int dep) {
    sz[u]=1;
    for (int i=0;i<26;++i)
        if (trie[u][i]) {
            dfs(trie[u][i],dep+1);
            sz[u]+=sz[trie[u][i]];
        }
    cnt[dep]+=sz[u];
    tot=n;
    int rt=0;
    for (int i=0;i<26;++i)
        rt=merge(rt,trie[u][i]);
    cnt[dep]-=sz[rt];
}
int main()
{
    scanf("%d",&n);
    for (int i=0;i<n-1;++i) {
        scanf("%d%d%s",&u,&v,op);
        trie[u][op[0]-'a']=v;
    }
    sz[0]=1;
    dfs(1,0);
    res=0;
    for (int i=1;i<n;++i)
        if (cnt[i]>cnt[res])
            res=i;
    printf("%d\n%d\n",sz[1]-cnt[res],res+1);
    return 0;
}


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=300005;
int n,u,v,x,trie[maxn<<1][26],sz[maxn<<1],tot,cnt[maxn],res;
char op[2];
int merge(int a,int b) {
    if (!a||!b)
        return a+b;
    int rt=++tot;
    memset(trie[rt],0,sizeof trie[rt]);
    sz[rt]=1;
    for (int i=0;i<26;++i) {
        trie[rt][i]=merge(trie[a][i],trie[b][i]);
        sz[rt]+=sz[trie[rt][i]];
    }
    return rt;
}
void dfs(int u,int dep) {
    sz[u]=1;
    for (int i=0;i<26;++i)
        if (trie[u][i]) {
            dfs(trie[u][i],dep+1);
            sz[u]+=sz[trie[u][i]];
        }
    cnt[dep]+=sz[u];
    tot=n;
    int rt=0;
    for (int i=0;i<26;++i)
        rt=merge(rt,trie[u][i]);
    cnt[dep]-=sz[rt];
}
int main()
{
    scanf("%d",&n);
    for (int i=0;i<n-1;++i) {
        scanf("%d%d%s",&u,&v,op);
        trie[u][op[0]-'a']=v;
    }
    dfs(1,0);
    res=0;
    for (int i=1;i<n;++i)
        if (cnt[i]>cnt[res])
            res=i;
    if (!cnt[res])
        ++cnt[res];
    printf("%d\n%d\n",sz[1]-cnt[res],res+1);
    return 0;
}


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=300005;
int n,u,v,x,trie[maxn<<1][26],sz[maxn<<1],tot,cnt[maxn],res;
char op[2];
int merge(int a,int b) {
    if (!a||!b)
        return a+b;
    int rt=++tot;
    memset(trie[rt],0,sizeof trie[rt]);
    sz[rt]=1;
    for (int i=0;i<26;++i) {
        trie[rt][i]=merge(trie[a][i],trie[b][i]);
        sz[rt]+=sz[trie[rt][i]];
    }
    return rt;
}
void dfs(int u,int dep) {
    sz[u]=1;
    for (int i=0;i<26;++i)
        if (trie[u][i]) {
            dfs(trie[u][i],dep+1);
            sz[u]+=sz[trie[u][i]];
        }
    cnt[dep]+=sz[u];
    tot=n;
    int rt=0;
    for (int i=0;i<26;++i)
        rt=merge(rt,trie[u][i]);
    cnt[dep]-=max(1,sz[rt]);
}
int main()
{
    scanf("%d",&n);
    for (int i=0;i<n-1;++i) {
        scanf("%d%d%s",&u,&v,op);
        trie[u][op[0]-'a']=v;
    }
    dfs(1,0);
    res=0;
    for (int i=1;i<n;++i)
        if (cnt[i]>cnt[res])
            res=i;
    printf("%d\n%d\n",sz[1]-cnt[res],res+1);
    return 0;
}


#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <time.h>
#include <vector>
#define ff first
#define ss second
#define MP make_pair
#define y1 wasdasfnasoifan
#define ls (o << 1)
#define rs (o << 1 | 1)
#define eps (1e-7)
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int inf = 1e9;
int n;
char s[20];
int ans[300100];
vector<int> dep[300100];
int G[300100][26], siz[300100], son[300100];
vector<int *> a;
void dfs(int o, int depth) {
    dep[depth].push_back(o);
    siz[o] = 1;
    for (int i = 0; i < 26; ++i) {
        if (G[o][i] == -1)
            continue;
        dfs(G[o][i], depth + 1);
        if (son[o] == -1 || siz[son[o]] < siz[G[o][i]]) {
            son[o] = G[o][i];
        }
        siz[o] += siz[G[o][i]];
    }
}
int DFS(int u, int v) {
    int ret = 0;
    for (int i = 0; i < 26; ++i) {
        if (G[u][i] == -1)
            continue;
        if (G[v][i] == -1) {
            G[v][i] = G[u][i];
            ret += siz[G[u][i]];
            a.push_back(&G[v][i]);
        } else {
            ret += DFS(G[u][i], G[v][i]);
        }
    }
    return ret;
}
int main() {
    memset(G, -1, sizeof(G));
    memset(son, -1, sizeof(son));
    scanf("%d", &n);
    for (int i = 1; i < n; ++i) {
        int u, v;
        scanf("%d%d%s", &u, &v, s);
        G[u][s[0] - 'a'] = v;
    }
    dfs(1, 0);
    int tmp = 0;
    for (int i = 0; i <= n; ++i) {
        a.clear();
        ans[i] += tmp;
        for (auto &j : dep[i]) {
            if (son[j] != -1)
                ans[i + 1] += siz[son[j]] - 1;
            for (int k = 0; k < 26; ++k) {
                if (G[j][k] == -1 || son[j] == G[j][k])
                    continue;
                ans[i + 1] += DFS(G[j][k], son[j]);
            }
        }
        for (auto &i : a) {
            *i = -1;
        }
        tmp += (int)dep[i].size();
    }
    //    for(int i = 1; i <= n; ++i){
    //        cout << ans[i] <<" ";
    //    }
    //    cout << endl;
    tmp = 1;
    for (int i = 2; i <= n; ++i) {
        if (ans[tmp] > ans[i]) {
            tmp = i;
        }
    }
    printf("%d\n%d\n", ans[tmp], tmp);
    return 0;
}


#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define MAXN 300010
#define inf 100000000
using namespace std;

int sz[MAXN],heavy[MAXN],edge[MAXN][30],ans,ans1,ans2,res=inf,ind,mxdpt;
vector<pair<int,int>>adj[MAXN],rollbk;
vector<int>depth[MAXN];

void dfsinit(int node,int par,int dpt){
	sz[node]=1;depth[dpt].pb(node);mxdpt=max(mxdpt,dpt);
	for(int i=0;i<adj[node].size();i++){
		int child=adj[node][i].first;
		int val=adj[node][i].second;
		if(child!=par){
			edge[node][val]=child;
			dfsinit(child,node,dpt+1);
			if(sz[child]>sz[heavy[node]]){
				heavy[node]=child;
			}
			sz[node]+=sz[child];
		}
	}
}

void merge(int node,int super){
	for(int i=0;i<26;i++){
		if(edge[node][i] && !edge[super][i]){
			edge[super][i]=edge[node][i];
			rollbk.pb(mp(super,i));
			ans+=sz[edge[node][i]];
		}
		else if(edge[node][i] && edge[super][i]){
			merge(edge[node][i],edge[super][i]);
		}
	}
}

void rollback(){
	for(int i=0;i<rollbk.size();i++){
		edge[rollbk[i].first][rollbk[i].second]=0;
	}
}

void dpthtrv(int dpt){
	if(dpt==mxdpt)
		return ;
	for(int j=0;j<depth[dpt].size();j++){
		int node=depth[dpt][j];
		ans=sz[heavy[node]];
		ans2+=sz[node];
		for(int i=0;i<26;i++){
			if(edge[node][i]!=heavy[node] && edge[node][i]){
				merge(edge[node][i],heavy[node]);
			}
		}
		ans1+=ans;
		ans=0;
		rollback();
		rollbk.clear();
	}
	if(sz[1]-ans2+ans1<res){
		res=sz[1]-ans2+ans1;
		ind=dpt;
	}
	ans1=ans2=ans=0;
	dpthtrv(dpt+1);
}

int main(){
	ios_base::sync_with_stdio(false);
	int n,u,v;
	char x;
	cin>>n;
	for(int i=1;i<n;i++){
		cin>>u>>v>>x;
		adj[u].pb(mp(v,x-'a'));
		adj[v].pb(mp(u,x-'a'));
	}
	dfsinit(1,-1,1);
	dpthtrv(1);
	cout<<res<<"\n"<<ind<<endl;
	return 0;
}


#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define MAXN 300010
#define inf 100000000
using namespace std;

int sz[MAXN],heavy[MAXN],edge[MAXN][30],ans,ans1,ans2,res=inf,ind,mxdpt;
vector<pair<int,int>>adj[MAXN],rollbk;
vector<int>depth[MAXN];

void dfsinit(int node,int par,int dpt){
	sz[node]=1;depth[dpt].pb(node);mxdpt=max(mxdpt,dpt);
	for(int i=0;i<adj[node].size();i++){
		int child=adj[node][i].first;
		int val=adj[node][i].second;
		if(child!=par){
			edge[node][val]=child;
			dfsinit(child,node,dpt+1);
			if(sz[child]>sz[heavy[node]]){
				heavy[node]=child;
			}
			sz[node]+=sz[child];
		}
	}
}

void merge(int node,int super){
	for(int i=0;i<26;i++){
		if(edge[node][i] && !edge[super][i]){
			edge[super][i]=edge[node][i];
			rollbk.pb(mp(super,i));
			ans+=sz[edge[node][i]];
		}
		else if(edge[node][i] && edge[super][i]){
			merge(edge[node][i],edge[super][i]);
		}
	}
}

void rollback(){
	for(int i=0;i<rollbk.size();i++){
		edge[rollbk[i].first][rollbk[i].second]=0;
	}
}

void dpthtrv(int dpt){
	if(dpt>mxdpt)
		return ;
	for(int j=0;j<depth[dpt].size();j++){
		int node=depth[dpt][j];
		ans=sz[heavy[node]];
		ans2+=sz[node];
		for(int i=0;i<26;i++){
			if(edge[node][i]!=heavy[node] && edge[node][i]){
				merge(edge[node][i],heavy[node]);
			}
		}
		ans1+=ans;
		ans=0;
		rollback();
		rollbk.clear();
	}
	if(sz[1]-ans2+ans1<res){
		res=sz[1]-ans2+ans1;
		ind=dpt;
	}
	ans1=ans2=ans=0;
	dpthtrv(dpt+1);
}

int main(){
	ios_base::sync_with_stdio(false);
	int n,u,v;
	char x;
	cin>>n;
	for(int i=1;i<n;i++){
		cin>>u>>v>>x;
		adj[u].pb(mp(v,x-'a'));
		adj[v].pb(mp(u,x-'a'));
	}
	dfsinit(1,-1,1);
	dpthtrv(1);
	cout<<res<<"\n"<<ind<<endl;
	return 0;
}


#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define MAXN 300010
#define inf 100000000
using namespace std;

int sz[MAXN],heavy[MAXN],edge[MAXN][30],ans,ans1,ans2,res=inf,ind,mxdpt;
vector<pair<int,int>>adj[MAXN],rollbk;
vector<int>depth[MAXN];

void dfsinit(int node,int par,int dpt){
	sz[node]=1;depth[dpt].pb(node);mxdpt=max(mxdpt,dpt);
	for(int i=0;i<adj[node].size();i++){
		int child=adj[node][i].first;
		int val=adj[node][i].second;
		if(child!=par){
			edge[node][val]=child;
			dfsinit(child,node,dpt+1);
			if(sz[child]>sz[heavy[node]]){
				heavy[node]=child;
			}
			sz[node]+=sz[child];
		}
	}
}

void merge(int node,int super){
	for(int i=0;i<26;i++){
		if(edge[node][i] && !edge[super][i]){
			edge[super][i]=edge[node][i];
			rollbk.pb(mp(super,i));
			ans+=sz[edge[node][i]];
		}
		else if(!edge[node][i] && edge[super][i]){
			ans+=sz[edge[super][i]];
		}
		else if(edge[node][i] && edge[super][i]){
			ans++;
			merge(edge[node][i],edge[super][i]);
		}
	}
}

void rollback(){
	for(int i=0;i<rollbk.size();i++){
		edge[rollbk[i].first][rollbk[i].second]=0;
	}
}

void dpthtrv(int dpt){
	if(dpt==mxdpt)
		return ;
	for(int i=0;i<depth[dpt].size();i++){
		int node=depth[dpt][i];
		for(int i=0;i<26;i++){
			ans2+=sz[edge[node][i]];
			if(edge[node][i]!=heavy[node] && edge[node][i]){
				ans=0;
				merge(edge[node][i],heavy[node]);
			}
		}
		if(ans==0)
			ans=sz[heavy[node]]-1;
		ans1+=ans;
		ans=0;
		rollback();
		rollbk.clear();
	}
	if(sz[1]-ans2+ans1<res){
		res=sz[1]-ans2+ans1;
		ind=dpt;
	}
	ans1=ans2=0;
	dpthtrv(dpt+1);
}

int main(){
	ios_base::sync_with_stdio(false);
	int n,u,v;
	char x;
	cin>>n;
	for(int i=1;i<n;i++){
		cin>>u>>v>>x;
		adj[u].pb(mp(v,x-'a'));
		adj[v].pb(mp(u,x-'a'));
	}
	dfsinit(1,-1,1);
	dpthtrv(1);
	cout<<res<<"\n"<<ind<<endl;
	return 0;
}