Problem:
Problem I: よくわかる二重魔法

紀慧暦2119 年15 の月。宮廷魔術士Sengemon Lugene の手によって、一冊の魔術書が書き記された。この魔術書「In Magiray」は、この世界の魔術法則の根幹に深く関わる「二重魔法」を、誰にでも習得可能な技術として体系化した、という点で革新的な書物であった。まずは、この書物の内容を要約したものを見ていこう。

【エレメント】

世界中のあらゆる物体は、「泉」「風」「羽」「花」「砂」「灯」……といった、極小要素「エレメント」から構成されている。エレメントには多くの種類があり、人間はエレメントと契約を交わすことで、契約を交わした種類のエレメントを用いた二重魔法を発動させることができる。

【二重魔法】

二重魔法とは、その名の通り、２つのエレメントを重ね合わせて解き放つ魔法である。主となるエレ
メントを「主属性」、もう一方を「副属性」と呼ぶ。たとえば、主属性が「灯」で副属性が「花」な
ら、その二重魔法は（灯，花）と表記される。（灯，花）と（花，灯）は別の二重魔法である。また、主属性と副属性は同じであってもよい。

【キーワード】

「泉→ huda」「風→ loar」「羽→ sheza」「花→ leide」といったように、各々のエレメントに、それぞれ短い「キーワード」を１対１対応させることができる。キーワードは、エレメントの持つ性質を短い言葉に象徴させることで、いくつかの連続するキーワード列を唱えるだけで簡単に二重魔法を発動させることを可能にした、画期的な概念である。

【呪文】

「huda・leide・loar・sheza」のように、１つ以上のキーワードを連続して並べたものを「呪文」という。術者が意図した呪文を唱えることで、対応する二重魔法を発動させることができる。具体的には、呪文の一番最初のキーワードに対応するエレメントを主属性、一番最後のキーワードに対応するエレメントを副属性とする二重魔法が発動する。先ほどの呪文の例だと、発動する二重魔法は（泉，羽）である。なお、呪文が１つのキーワードだけから成る場合には、主属性も副属性も、そのキーワードに対応するエレメントになる。

【エレメント同士の相性】

「huda・leide・loar・sheza」の呪文で（泉，羽）の二重魔法が発動することはすでに述べた。しかし、一見すると、（泉，羽）を使いたいのならば、わざわざ長い呪文を唱えずとも「huda・sheza」だけで良いようにも思える。だが実際には、それは不可能である。なぜなら、「泉（huda）」と「羽（sheza）」は相性が良くないため、これら２つが隣り合った呪文を唱えると、反発作用が起こってエレメント同士が相殺してしまうからである。一方で、「泉（huda）」と「花（leide）」、「花（leide）」と「風（loar）」、「風（loar）」と「羽（sheza）」は、どのペアも好相性なので、「huda・leide・loar・sheza」や「loar・leide・huda」といった呪文では、エレメントの相殺は起こらない。

【呪文論理】

二重魔法を使おうとする者は、あらかじめ、すべての好相性なエレメントのペアそれぞれに対して、
どちらのキーワードが「上位ワード」で、どちらのキーワードが「下位ワード」なのかを定義しておかなければならない。好相性な２つのエレメントのキーワード「A」と「B」に対して、A を上位ワード、B を下位ワードと定義したとき、A > B と表記する。A > B であるとき、一つの呪文内で、B の直後にA を並べることはできない。つまり、「A1・A2・A3・……・Ak」が正しい呪文であるならば、A1 > A2，A2 > A3，……，Ak-1 > Ak でなくてはならない。ちなみに、A > B かつB > C であっても、A > C である必要はない。

このように、各々の術者が、上位・下位ワードを自分好みに定義することを「呪文論理の構築」と呼
ぶ。これは、あえて呪文の構文に制約をもたせることで、エレメントの流れを明確化し、目的の二重
魔法の成功確率を上げるための工夫である。だが、その代償として、呪文論理を構築すると、どのよ
うに呪文を唱えても発動させることのできない二重魔法が生じてしまう可能性がある（ただし、いか
なる呪文論理を構築しても使うことのできない二重魔法は存在しないことが証明されている）。この
ような事情があるため、どのような呪文論理を構築するかは、今も昔も多くの魔術士を悩ませている
厄介な問題なのである。

とある小さな町で暮らす見習い魔術士の少年ユートは、すべてのエレメントとの契約を終え、いよい
よ明日、自分の呪文論理を構築するための儀式を行おうとしている。ユートは単純に、できるだけた
くさんの種類の二重魔法を発動させることができるような呪文論理が良い呪文論理である、と考えて
いた。

超一級電術技師（現代日本で言うところのスーパープログラマー）であるあなたは、友達のユートか
ら、うまく呪文論理を構築することができれば、最大で何種類の二重魔法を使えるようになるのか知
りたいと相談を受けた。これは、今まで世界中の人間が挑戦してきたが、誰も解くことができなかっ
たほどの難問である。もしも解くことができれば、あなたの名前は未来永劫、世界中で語り継がれる
ことになるであろう。

Input

N M

s1

s2

.

.

.

sN

p1 q1
p2 q2
.

.

.

pM M

入力の１行目には、整数N と整数M が、空白区切りで書かれている。これは、エレメントが全部でN 種類、好相性なエレメントのペアが全部でM 組存在することをあらわす。

続くN 行には、１つの文字列が書かれている。１＋ i 行目に書かれた文字列si は、i 番目のエレメントに対応するキーワードがsi であることをあらわす。

続くM 行には、２つの文字列が空白区切りで書かれている。１＋ N ＋ i 行目に書かれた文字列pi とqi は、キーワードpi に対応するエレメントと、キーワードqi に対応するエレメントのペアが、好相性であることをあらわす。逆に、ここにあらわれなかったエレメントのペアは、好相性ではない。

これらのデータは、以下の条件をみたす。

2 ≤ N ≤ 100

1 ≤ M ≤ 4,950

si は、アルファベットの大文字または小文字のみからなる、1 文字以上15 文字以下の文字列

i ≠ j 　⇒　si ≠ sj

pi ≤ qi

i ≤ j 　⇒　(pi, qi) ≠ (pj, qj) かつ(pi, qi) ≠ (qj, pj)

いかなる呪文論理を構築しても発動することのできない二重魔法は存在しない

Output

呪文論理を構築したとき、使用可能になる二重魔法の種類の最大数を出力せよ。

Sample Input 1

4 3
huda
leide
loar
sheza
huda leide
leide loar
loar sheza

Sample Output 1

10

Sample Input 2

3 3
torn
siole
dimi
torn siole
torn dimi
siole dimi

Sample Output 2

9

Sample Input 3

9 10
riris
soa
elah
clar
arma
memori
neoles
qhaon
clue
clar riris
riris memori
riris neoles
soa clar
soa memori
neoles elah
elah qhaon
neoles qhaon
clue riris
arma elah

Sample Output 3

54

Solution in C# (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;
using Debug = System.Diagnostics.Debug;
//using System.Numerics;
//using static System.Math;
using Number = System.Int64;

namespace Program {
    public class Solver {
        //const long MOD = (long)1e9 + 7;
        public void Solve() {
            var n = ri;
            var m = ri;
            var id = new HashMap<string, int>();
            for (int i = 0; i < n; i++)
                id[rs] = i;
            var g = new BridgeGraph(n);
            var f = new int[m];
            var t = new int[m];
            for (int i = 0; i < m; i++) {
                f[i] = id[rs]; t[i] = id[rs];
                g.AddEdge(f[i], t[i]);
            }
            g.Build();
            solve(f, t, g);
        }
        void solve(int[] ff, int[] tt, BridgeGraph g) {
            var n = g.Size;
            var G = Enumerate(n, x => new List<int>());
            for (int i = 0; i < ff.Length; i++)
                if (!g.IsSame(ff[i], tt[i])) {
                    G[g[ff[i]]].Add(g[tt[i]]);
                    G[g[tt[i]]].Add(g[ff[i]]);
                }
            var sz = Enumerate(n, x => g.Components[x].Count);

            Func<int, int, int[][]> dfs = null;
            dfs = (prev, cur) => {
                var sum = sz[cur];
                var ret = Enumerate(sum + 1, x => new int[sum + 1]);
                ret[sum][sum] = sum * sum;
                foreach (var t in G[cur]) {
                    if (prev == t) continue;
                    var res = dfs(cur, t);
                    sum += res.Length - 1;
                    var next = Enumerate(sum + 1, x => new int[sum + 1]);
                    for (int i = 0; i < ret.Length; i++)
                        for (int j = 0; j < ret.Length; j++)
                            for (int k = 0; k < res.Length; k++)
                                if (ret[i][j] != 0)
                                    for (int l = 0; l < res.Length; l++) {
                                        if (res[k][l] != 0) {
                                            next[i][j + l] = Math.Max(next[i][j + l], ret[i][j] + res[k][l] + i * l);
                                            next[i + k][j] = Math.Max(next[i + k][j], ret[i][j] + res[k][l] + j * k);
                                        }
                                    }
                    ret = next;
                }
                return ret;
            };
            var ans = dfs(-1, 0);
            var max = ans.SelectMany(x => x).Max();
            //foreach (var x in ans) Console.WriteLine(x.AsJoinedString("\t"));
            Console.WriteLine(max);
        }
        const long INF = 1L << 60;
        //int[] dx = { 1, 0, -1, 0 };
        //int[] dy = { 0, 1, 0, -1 };
        //*
        int ri { get { return sc.Integer(); } }
        long rl { get { return sc.Long(); } }
        double rd { get { return sc.Double(); } }
        string rs { get { return sc.Scan(); } }
        //*/

        public IO.StreamScanner sc = new IO.StreamScanner(Console.OpenStandardInput());
        static T[] Enumerate<T>(int n, Func<int, T> f) { var a = new T[n]; for (int i = 0; i < n; ++i) a[i] = f(i); return a; }
        static public void Swap<T>(ref T a, ref T b) { var tmp = a; a = b; b = tmp; }
    }
}

#region main
static class Ex {
    static public string AsString(this IEnumerable<char> ie) { return new string(ie.ToArray()); }
    static public string AsJoinedString<T>(this IEnumerable<T> ie, string st = " ") {
        return string.Join(st, ie.Select(x => x.ToString()).ToArray());
        //return string.Join(st, ie);
    }
    static public void Main() {
        Console.SetOut(new System.IO.StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false });
        var solver = new Program.Solver();
        solver.Solve();
        Console.Out.Flush();
    }
}
#endregion
#region Ex
namespace Program.IO {
    using System.Globalization;
    using System.IO;
    using System.Text;
    public class Printer: StreamWriter {
        public override IFormatProvider FormatProvider { get { return CultureInfo.InvariantCulture; } }
        public Printer(Stream stream) : base(stream, new UTF8Encoding(false, true)) { }
    }
    public class StreamScanner {
        public StreamScanner(Stream stream) { str = stream; }
        public readonly Stream str;
        private readonly byte[] buf = new byte[1024];
        private int len, ptr;
        public bool isEof = false;
        public bool IsEndOfStream { get { return isEof; } }
        private byte read() {
            if (isEof) return 0;
            if (ptr >= len) { ptr = 0; if ((len = str.Read(buf, 0, 1024)) <= 0) { isEof = true; return 0; } }
            return buf[ptr++];
        }
        public char Char() { byte b = 0; do b = read(); while ((b < 33 || 126 < b) && !isEof); return (char)b; }

        public string Scan() {
            var sb = new StringBuilder();
            for (var b = Char(); b >= 33 && b <= 126; b = (char)read())
                sb.Append(b);
            return sb.ToString();
        }
        public string ScanLine() {
            var sb = new StringBuilder();
            for (var b = Char(); b != '\n' && b != 0; b = (char)read())
                if (b != '\r') sb.Append(b);
            return sb.ToString();
        }
        public long Long() { return isEof ? long.MinValue : long.Parse(Scan()); }
        public int Integer() { return isEof ? int.MinValue : int.Parse(Scan()); }
        public double Double() { return isEof ? double.NaN : double.Parse(Scan(), CultureInfo.InvariantCulture); }
    }
}
#endregion
public class BridgeGraph {
    int n;
    List<int> E;
    List<int>[] g;

    int[] used;
    int[] val;
    int[] group;

    /// <summary>
    /// ???????????£???????????????????????????????????????
    /// </summary>
    public List<List<int>> Components = new List<List<int>>();
    public int Size { get { return Components.Count; } }
    public int this[int index] { get { return group[index]; } }


    /// <summary>
    /// ???????????£?????????????§£????????????????????°???????????????????????°??????????????£??????????????¨??????????????¨??????????????????????????£????????????????????????
    /// </summary>
    public BridgeGraph(int N) {
        n = N;
        g = new List<int>[n];
        for (int i = 0; i < n; i++)
            g[i] = new List<int>();
        E = new List<int>();
    }
    public void AddEdge(int f, int t) {
        var id = E.Count;
        g[f].Add(id);
        g[t].Add(id);
        E.Add(f ^ t);
    }
    /// <summary>
    /// ???????????£?????????????§£?????????
    /// </summary>
    /// <returns></returns>
    public void Build() {
        Decomposite();
        group = new int[n];
        for (int i = 0; i < Components.Count; i++)
            foreach (var x in Components[i]) group[x] = i;
    }
    /// <summary>
    /// ???????????£?????????????????°?????????
    /// </summary>
    public int Decomposite() {
        used = new int[n];
        val = new int[n];
        var ptr = 0;
        for (int i = 0; i < n; i++)
            if (used[i] == 0) dfs(-1, i, ref ptr);
        used = new int[n];
        ptr = 0;
        for (int i = 0; i < n; i++)
            if (used[i] == 0) efs(-1, i, null, ref ptr);
        return Components.Count;
    }

    void dfs(int eid, int cur, ref int ptr) {
        used[cur] = ++ptr;
        foreach (var id in g[cur]) {
            if (id == eid) continue;
            var t = E[id] ^ cur;
            if (used[t] != 0) {
                if (used[t] < used[cur]) {
                    val[cur]++;
                    val[t]--;
                }
            }
            else {
                dfs(id, t, ref ptr);
                val[cur] += val[t];
            }

        }

    }
    void efs(int eid, int cur, List<int> component, ref int ptr) {
        if (component == null) {
            component = new List<int>();
            Components.Add(component);
        }
        used[cur] = ++ptr;
        component.Add(cur);
        foreach (var id in g[cur]) {
            if (id == eid) continue;
            var t = E[id] ^ cur;
            if (used[t] != 0) continue;
            if (val[t] == 0) efs(id, t, null, ref ptr);
            else efs(id, t, component, ref ptr);
        }
    }
    public bool IsSame(int u, int v) { return group[u] == group[v]; }

}
#region HashMap<K,V>
public class HashMap<K, V>: Dictionary<K, V>
//where V : new()
{
    public HashMap() : base() { }
    public HashMap(int cap) : base(cap) { }
    new public V this[K i] {
        get {
            V v;
            return TryGetValue(i, out v) ? v :
                base[i] = default(V);
            //base[i] = new V();
        }
        set { base[i] = value; }
    }
}
#endregion