Problem:
魔法陣

人里離れた森の奥深くに、マリーという名の魔女が住んでいました。彼女は魔女なので、食料・水・燃料など、生活に必要なものを魔法によってまかなっています。

彼女の魔法は、魔力を込めたいくつかの石と紐を使って、魔法陣を描くことによって発動します。この魔法陣は、石を設置し、いくつかの石のペアを紐で繋ぐことで描かれます。ただし、紐は弛んでいてはいけません。さらに、どの紐も、(両端を除いて) 他の紐や石に触れてはいけません。もちろん、同じ場所に複数の石を置くこともできません。この制約が守られる限り、石と石の位置関係や紐の長さに制限はありません。また、石は十分小さいので点として扱うことができ、紐は十分細いので線分として扱うことができます。さらに、彼女は同じ石のペアを2本以上の紐で繋ぐことも、ある紐の両端を同じ石に結ぶこともありません。

マリーは初め、石を彼女の家の平らな壁に貼り付けて魔法陣を描いていました。しかし、彼女はやがて、どうしても作ることのできない魔法陣があることに気付いたのです。しばらくして、彼女は魔法陣を平らな壁、つまり二次元平面の上で作ることができるか判定する方法を編み出しました。ある魔法陣は、次のような部分を含む場合、またその場合に限って、二次元平面上で作ることができないのです。

        

図１ 頂点数 5 の完全グラフ

図２ 頂点数 3, 3 の完全二部グラフ

このような魔法陣を描くために、彼女は石を空中に固定する魔法を編み出しました。三次元空間では、これらの魔法陣も描くことができるのです。それどころか、どんな魔法陣も三次元空間中では描くことができることを彼女は突き止めたのです。

さて、ここからが問題です。ある日マリーは、彼女の家の玄関に足元灯の魔法陣を作ることにしました。しかし、彼女は既に狭い玄関にいろいろな魔法陣を設置していたので、足元灯の魔法陣を、残された唯一のスペースである一次元の直線の上に描かなければなりません。彼女が設計したいくつかの足元灯の魔法陣について、それが直線の上に描けるかどうかを判定し、描ける場合 yes と、そうでない場合 no と出力するプログラムを作成して下さい。

魔法陣は、石の個数 n、紐の本数 m、m 個の紐の情報で与えられます。紐は、その両端の石の番号 u、v で表されます。石には 1 から n までの番号が与えられ、n は 1 以上 100,000 以下、m は 0 以上 1,000,000 以下とします。

入力

複数のデータセットの並びが入力として与えられます。入力の終わりはゼロふたつの行で示されます。各データセットは以下のとおりです。

1 行目 石の個数 n 紐の本数 m（整数 整数；半角空白区切り）

2 行目 第1の紐の情報 u v（整数 整数；半角空白区切り）

3 行目 第2の紐の情報

:

m+1 行目 第 m の紐の情報

データセットの数は 20 を超えません。

出力

入力データセットごとに、yes または no と１行に出力します。

入力例

5 4
1 2
2 3
3 4
4 5
4 6
1 2
1 3
1 4
2 3
2 4
3 4
5 0
0 0

出力例

yes
no
yes

Solution in Kotlin (judge verdict: Accepted)



fun main(args:Array<String>):Unit {
    while (true) {
        val (n, m) = readLine()!!.trim().split(' ').map(String::toInt)
        if (n == 0 && m == 0) return
        val stone = Array(n) { UnionFindTree() }
        val edgeCount = Array(n){0}
        var canMake = true
        repeat(m) {
            val (u, v) = readLine()!!.trim().split(' ').map(String::toInt)
            canMake = canMake && !stone[u - 1].isSameUnion(stone[v - 1])
            stone[u - 1].merge(stone[v - 1])
            edgeCount[u - 1]++
            edgeCount[v - 1]++
        }
        println(if (canMake && edgeCount.none{it > 2}) "yes" else "no")
    }
}
class UnionFindTree{
    private var mParent:UnionFindTree? = null
    private fun parent():UnionFindTree {
        return when(mParent){
            null -> this
            else -> {
                mParent = mParent?.parent()
                mParent !!
            }
        }
    }
    fun isSameUnion(other:UnionFindTree):Boolean {
        return parent() == other.parent()
    }
    fun merge(other:UnionFindTree):Unit {
        if (!isSameUnion(other)){
            parent().mParent = other.parent()
        }
    }
}