Problem:
MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]], processEscapes: true }});

夏合宿の朝は早い

JAG夏合宿の朝は早い．
正確にはそこまで早くないが，早いと感じる参加者が多いという．

例年合宿の会場となる施設では，退去時に参加者がシーツの回収や掃除をしなければならない．
1 つの部屋でも退去が遅れると，来年以降の施設の利用に関わるため，参加者は一人たりとも寝坊してはならない．

そうは言っても皆人間，寝坊することもある．
しかし，起きた人が連絡先を知っている人にモーニングコールをすることで，一人も寝坊しないよう努めることができるはずだ．

JAG夏合宿の運営を任されたあなたは，寝坊を絶対に防ぐ手を打つための前準備として，どのくらいの確率で全員がきちんと起きられるのかを調べることにした．
準備としてまず，各参加者の寝坊する確率と，各々が連絡先を知っている人のリストを入手した．
ここで，部屋は個室であるため，各々が寝坊するか否かは，他の参加者が寝坊するか否かとは独立である．
起きた人は必ずすべての知っている連絡先にモーニングコールをすること，また，モーニングコールを受けた人は必ず起きることを仮定するとき，全員がきちんと起きられる確率をこれらの情報から計算せよ．

Input

入力は複数のデータセットからなる． 各データセットは次の形式で表される．

N
p1 m1 a(1,1) ... a(1, m1)
...
pN mN a(N,1) ... a(N, mN)

N は参加者の数であり，100 を越えない正の整数である．pi は i 番目の参加者の寝坊する確率であり，小数点以下 2 桁以内の 0 以上 1 以下の実数である．mi は i 番目の参加者が知っている連絡先の数であり，0 以上 N 以下の整数である．a(i, j) は i 番目の参加者が知っている j 番目の連絡先が a(i, j) 番目の参加者のものであることを表す．a(i, j) は N を越えない正の整数である．

入力の終わりは，1 つのゼロからなる行で示す．

Output

各データセットについて，全員が起床できる確率を 1 行に出力せよ．出力には 0.00001 以上の誤差を含んではならない．

Sample Input

2
0.60 1 2
0.60 0
2
0.60 1 2
0.60 1 1
5
0.10 1 2
0.20 1 3
0.30 1 4
0.40 1 5
0.50 1 1
5
0.10 0
0.20 1 1
0.30 1 1
0.40 1 1
0.50 1 1
5
0.10 4 2 3 4 5
0.20 0
0.30 0
0.40 0
0.50 0
4
0.10 1 2
0.20 0
0.30 1 4
0.40 1 3
5
0.10 0
0.20 0
0.30 0
0.40 0
0.50 0
0

Output for Sample Input

0.400000000
0.640000000
0.998800000
0.168000000
0.900000000
0.792000000
0.151200000

Solution in Scala (judge verdict: Accepted)

import scala.annotation.tailrec
import scala.collection.{immutable, mutable}
object Main extends App {
  import scala.io.StdIn._

  loop()
  @tailrec def loop(): Unit = {
    val n = readInt()
    if (n == 0) return
    val member = Array.fill(n) {
      val prob :: m :: list = readLine.trim.split(' ').toList
      (prob.toDouble, list.map(_.toInt - 1))
    }
    val inverseEdge = Array.fill(n) {Nil: List[Int]}
    for (i ← member.indices; j ← member(i)._2) inverseEdge(j) ::= i
    val lastTime = Array.fill(n) {0}
    val isDiscovery = Array.fill(n) {false}
    def setLastTIme(current: Int, time: Int = 0): Int = {
      if (!isDiscovery(current)) {
        isDiscovery(current) = true
        lastTime(current) = member(current)._2.foldLeft(time) { case (t, next) ⇒ setLastTIme(next, t) } + 1
        lastTime(current)
      } else time
    }
    (0 until n).foldLeft(0){case (t, r) ⇒ setLastTIme(r, t)}
    isDiscovery.indices.foreach(i ⇒ isDiscovery(i) = false)
    val union = new UnionFind(n + 1)
    def mergeStrongConnect(current: Int): Unit = {
      isDiscovery(current) = true
      for (next ← inverseEdge(current) if !isDiscovery(next)) {
        union.merge(current, next)
        mergeStrongConnect(next)
      }
    }
    for (i ← lastTime.indices.sortBy(lastTime)(Ordering.Int.reverse) if !isDiscovery(i)) {
      mergeStrongConnect(i)
    }
    var result = 1d
    val inEdgeCount= inverseEdge.indices.map(i ⇒ inverseEdge(i).count(j ⇒ !union.isSame(i, j)))
    for (i ← inverseEdge.indices if !union.isSame(i, n)) {
      var res = 1d
      var inEdge = 0
      for (j ← 0 until n if union.isSame(i, j)) {
        res *= member(j)._1
        inEdge += inEdgeCount(j)
      }
      if (inEdge == 0) result *= (1 - res)
      union.merge(i, n)
    }
    println(f"$result%.9f")
    loop()
  }
}
class UnionFind(val size: Int) {
  private[this] val array = Array.fill(size){-1}
  def find(a: Int): Int = {
    if (array(a) < 0) a
    else {
      array(a) = find(array(a))
      array(a)
    }
  }
  def isSame(a: Int, b: Int): Boolean = find(a) == find(b)
  def merge(a: Int, b: Int): Unit = {
    val ar = find(a)
    val br = find(b)
    if (ar != br){
      if (array(ar) < array(br)) {
        array(ar) += array(br)
        array(br) = ar
      }else {
        array(br) += array(ar)
        array(ar) = br
      }
    }
  }
}