Problem:
Problem B: Matrix Operations

Problem

要素が1と0からなるn×nの行列に対して、以下の種類の操作を行うプログラムを作成せよ。

部分行列と角度(0,90,180,270,360)が与えられるのでその分だけ時計回りに回転させる。

部分行列が与えられるので値を反転させる。

行が与えられるので左に1個シフトし、はみ出た分は右端に移動する。

行が与えられるので右に１個シフトし、はみ出た分は左端に移動する。

行(r),列(c)が与えられる。

Step1：現在のセルを(r,c)にする。

Step2：現在のセルの値を反転する。

Step3：隣接するセルのうち、値が現在のセルの反転前の値と等しいセルに対し、Step2とStep3の処理を行う。

ここで、セル(i,j)に隣接するセルとは、(i-1,j),
(i+1,j),(i,j-1),(i,j+1)の４つである。

Input

n m
a1,1 a1,2 ... a1,n
a2,1 a2,2 ... a2,n
:
an,1 an,2 ... an,n
Operation1
Operation2
:
Operationm

入力の最初に2つの整数nとmが空白区切りで与えられる。 nは行列のサイズ、mは操作の数を表している。

次にグリッドの情報がn行で与えられ、グリッドの情報のi行目にはn個の整数
ai,jが空白区切りで与えられる。ai,jには0か1が与えられる。

次に1行で表される各Operationiがm個与えられる。
Operationi の最初の入力はo(操作の種類)が入力される。

oが0のときは回転(Rotate)を表している。
続く入力は、r c size angle(行、列、サイズ、角度) が空白区切りで与えられる。
左上(r,c)から右下(r+size-1,c+size-1)が部分行列となる。
正方行列になることは保証されている。

oが1のときは反転(Reversal)を表している。
また、続く入力は、r c size (行、列、サイズ) が空白区切りで与えられる。
左上(r,c)から右下(r+size-1,c+size-1)が部分行列となる。
正方行列になることは保証されている。

oが2のときは左シフト(Left shift)を表している。
また、続く入力は r(行) が与えられる。

oが3のときは右シフト(Right shift)を表している。
また、続く入力は r(行) が与えられる。

oが4のときは島反転(Island reversal)を表している。
また、続く入力は r c(行、列) が空白区切りで与えられる。

Constraints

入力は以下の条件を満たす。

1 ≤ n,r,c,size ≤ 15

1 ≤ m ≤ 100

0 ≤ o ≤ 4

1 ≤ r+size-1,c+size-1 ≤ 15

0 ≤ angle ≤ 360

Output

生成された行列を出力して下さい。

Sample Input 1

3 1
1 1 0
0 1 1
1 0 1
0 2 2 2 90

Sample Output 1

1 1 0
0 0 1
1 1 1

Sample Input 2

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

Sample Output 2

0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0

Sample Input 3

5 2
1 1 1 0 1
1 0 0 0 0
0 0 0 1 1
0 1 0 0 0
0 1 1 1 0
1 3 3 3
0 1 1 5 90

Sample Output 3

0 0 0 1 1
1 1 0 0 1
0 1 1 0 1
0 1 0 0 0
1 1 0 0 1

Solution in Scala (judge verdict: Accepted)

import Indexer.{ManualIndexer, Rotate90, Shifted}

import scala.annotation.tailrec
import scala.collection.mutable
object Main extends App {
  import scala.io.StdIn._
  val Array(n, m) = readLine.trim.split(' ').map(_.toInt)
  val matrix = new Matrix(Array.fill(n){readLine.trim.split(' ').map(_.toInt)})
  for (_ ← 0 until m){
    readLine.trim.split(' ').map(_.toInt) match {
      case Array(0, r, c, size, angle) ⇒ matrix.rotate(c, r, size, angle)
      case Array(1, r, c, size) ⇒ matrix.reversal(c, r, size)
      case Array(2, r) ⇒ matrix.shiftLeft(r)
      case Array(3, r) ⇒ matrix.shiftRight(r)
      case Array(4, r, c) ⇒ matrix.islandFlip(c, r)
      case _ ⇒ ???
    }
  }
  println(
    (0 until n).map(r ⇒ (0 until n).map(c ⇒ matrix(c, r)).mkString(" ")).mkString("\n")
  )

}
class Matrix(private val state: Array[Array[Int]]) {
  private var indexer: Indexer = Indexer(state.length, state.head.length)
  def apply(x: Int, y: Int): Int = {
    val (nx, ny) = indexer.convert(x, y)
    state(ny)(nx)
  }
  def update(x: Int, y: Int, value: Int): Unit = {
    val (nx, ny) = indexer.convert(x, y)
    state(ny)(nx) = value
  }
  def isDefined(x: Int, y: Int): Boolean = state.indices.contains(y) && state.head.indices.contains(x)
  def rotate(x: Int, y: Int, size: Int, angle: Int): Unit = {
    angle match {
      case 90 ⇒
        indexer = indexer.partialRotated90(x - 1, y - 1, size)
      case 180 ⇒
        indexer = indexer.partialRotated90(x - 1, y - 1, size).partialRotated90(x - 1, y - 1, size)
      case 270 ⇒
        indexer = indexer.partialRotate270(x - 1, y - 1, size)
      case _ ⇒ Unit
    }
  }
  def reversal(x: Int, y: Int, size: Int): Unit = {
    for (i ← 0 until size; j ← 0 until size) {
      this(i + x - 1, j + y - 1) = 1 - this(i + x - 1, j + y - 1)
    }
  }
  def shiftLeft(row: Int): Unit = {
    indexer = indexer.rowShifted(row - 1)
  }
  def shiftRight(row: Int): Unit = {
    indexer = indexer.rowShiftedReverse(row - 1)
  }
  def islandFlip(x: Int, y: Int): Unit = {
    val queue = mutable.Queue[(Int, Int)]((x - 1, y - 1))
    val initial = this(x - 1, y - 1)
    this(x - 1, y - 1) = 1 - initial
    while(queue.nonEmpty) {
      val (nx, ny) = queue.dequeue()
      for (dx ← -1 to 1 by 2 if isDefined(nx + dx, ny) && this(nx + dx, ny) == initial) {
        this(nx + dx, ny) = 1 - initial
        queue.enqueue((nx + dx, ny))
      }
      for (dy ← -1 to 1 by 2 if isDefined(nx, ny + dy) && this(nx, ny + dy) == initial) {
        this(nx, ny + dy) = 1 - initial
        queue.enqueue((nx, ny + dy))
      }
    }
  }
}
trait Indexer {
  def indices: Iterable[(Int, Int)] = (0 until width).flatMap(x ⇒ (0 until height).map(y ⇒ (x, y)))
  def height: Int
  def width: Int
  def convert(x: Int, y: Int): (Int, Int)
  protected def shifted(dx: Int, dy: Int): Indexer = new Shifted(dx, dy, this).flattenIfDeep
  protected def rotated90: Indexer = new Rotate90(this).flattenIfDeep
  def partialRotated90(_x: Int, _y: Int, size: Int): Indexer = {
    val partial = Indexer(size, size).rotated90.shifted(_x, _y)
    val array = Array.fill(height){Array.fill(width){(0, 0)}}
    for ((x, y) ← indices) {
      array(y)(x) = if (partial.isDefined(x, y)) {
        val (nx, ny) = partial.convert(x, y)
        convert(nx, ny)
      }
      else convert(x, y)
    }
    new ManualIndexer(array)
  }
  def partialRotate270(_x: Int, _y: Int, size: Int): Indexer = {
    val partial = Indexer(size, size).rotated90.rotated90.rotated90.shifted(_x, _y)
    val array = Array.fill(height){Array.fill(width){(0, 0)}}
    for ((x, y) ← indices) {
      array(y)(x) = if (partial.isDefined(x, y)) {
        val (nx, ny) = partial.convert(x, y)
        convert(nx, ny)
      }
      else convert(x, y)
    }
    new ManualIndexer(array)
  }
  def rowShifted(row: Int): Indexer = flatten.rowShifted(row, 1)
  def rowShiftedReverse(row: Int): Indexer = flatten.rowShifted(row, width - 1)
  protected def rowShifted(row: Int, count: Int): Indexer = flatten.rowShifted(row, count)
  protected def isDefined(x: Int, y: Int): Boolean = (0 until height).contains(y) && (0 until width).contains(x)
  protected def depth: Int
  protected def flatten: Indexer = {
    val array = Array.fill(height){Array.fill(width){(0, 0)}}
    for ((x, y) ← indices) {
      array(y)(x) = convert(x, y)
    }
    new ManualIndexer(array)
  }
  protected def flattenIfDeep: Indexer = if (depth > 10) flatten else this
}
object Indexer {
  def apply(width: Int, height: Int): Indexer = new Pure(height, width)
  private class Pure(override val height: Int, override val width: Int) extends Indexer {
    override def convert(x: Int, y: Int): (Int, Int) = (x, y)
    override protected def depth: Int = 0
  }
  private class ManualIndexer(val map: Array[Array[(Int, Int)]]) extends Indexer {
    override def height: Int = map.length
    override def width: Int = map.head.length
    override def convert(x: Int, y: Int): (Int, Int) = map(y)(x)
    override protected def rowShifted(row: Int, count: Int): Indexer = new ManualIndexer(map.indices.map(i ⇒ if (i == row) map(row).indices.map(j ⇒ map(row)((j + count) % map(row).length)).toArray else map(i)).toArray)
    override protected def depth: Int = 0
  }
  private class Shifted(val dx: Int, val dy: Int, val indexer: Indexer) extends Indexer {
    override def height: Int = indexer.height
    override def width: Int = indexer.width
    override def convert(x: Int, y: Int): (Int, Int) = indexer.convert(x - dx, y - dy) match {case (nx, ny) ⇒ (nx + dx, ny + dy)}
    override protected def isDefined(x: Int, y: Int): Boolean = indexer.isDefined(x - dx, y - dy)
    override protected def depth: Int = indexer.depth + 1
  }
  private class Rotate90(val indexer: Indexer) extends Indexer {
    override def height: Int = indexer.width
    override def width: Int = indexer.height
    override def convert(x: Int, y: Int): (Int, Int) = indexer.convert(y, indexer.height - x - 1)
    override protected def isDefined(x: Int, y: Int): Boolean = indexer.isDefined(y, indexer.height - x - 1)
    override protected def depth: Int = indexer.depth + 1
  }

}