/******************************************************************/ /* ACM ICPC 2014-2015 */ /* Northeastern European Regional Contest */ /* St Petersburg - Barnaul - Tbilisi - Tashkent, December 7, 2014 */ /******************************************************************/ /* Problem E. Epic Win! */ /* */ /* Original idea Mikhail Dvorkin */ /* Problem statement Mikhail Dvorkin */ /* Test set Mikhail Dvorkin */ /******************************************************************/ /* Checker */ /* */ /* Author Mikhail Dvorkin */ /******************************************************************/ import ru.ifmo.testlib.Checker; import ru.ifmo.testlib.InStream; import ru.ifmo.testlib.Outcome; import static ru.ifmo.testlib.Outcome.Type.*; public class Check implements Checker { static final int MAX = 50000; static final String MOVES = "RPS"; static final int M = MOVES.length(); static final int ROUNDS = 1000000000; static final int ROUNDS_LOG = 32 - Integer.numberOfLeadingZeros(ROUNDS); static final int PERCENTAGE = 99; @Override public Outcome test(InStream inf, InStream ouf, @SuppressWarnings("unused") InStream ans) { FSM opponent = new FSM(inf, true); FSM player = new FSM(ouf, false); return check(opponent, player); } public static Outcome check(FSM opponent, FSM player) { int[][] oNext = new int[opponent.n][player.n]; int[][] pNext = new int[opponent.n][player.n]; int[][] score = new int[opponent.n][player.n]; int[][] noNext = new int[opponent.n][player.n]; int[][] npNext = new int[opponent.n][player.n]; int[][] nscore = new int[opponent.n][player.n]; int[] startO = new int[opponent.n]; int[] startP = new int[opponent.n]; int[] startS = new int[opponent.n]; for (int i = 0; i < opponent.n; ++i) { startO[i] = i; startP[i] = 0; startS[i] = 0; } for (int k = 0; k <= ROUNDS_LOG; ++k) { if (k == 0) { for (int o = 0; o < opponent.n; o++) { for (int p = 0; p < player.n; p++) { int oMove = opponent.move[o]; int pMove = player.move[p]; oNext[o][p] = opponent.next[o][pMove]; pNext[o][p] = player.next[p][oMove]; score[o][p] = (pMove - oMove + M + M / 2) % M > M / 2 ? 1 : 0; } } } else { for (int o = 0; o < opponent.n; o++) { for (int p = 0; p < player.n; p++) { int o1 = oNext[o][p]; int p1 = pNext[o][p]; noNext[o][p] = oNext[o1][p1]; npNext[o][p] = pNext[o1][p1]; nscore[o][p] = score[o][p] + score[o1][p1]; } } for (int o = 0; o < opponent.n; o++) { for (int p = 0; p < player.n; p++) { oNext[o][p] = noNext[o][p]; pNext[o][p] = npNext[o][p]; score[o][p] = nscore[o][p]; } } } if (((ROUNDS >> k) & 1) != 0) { for (int i = 0; i < opponent.n; i++) { int o = startO[i]; int p = startP[i]; int s = startS[i]; int o1 = oNext[o][p]; int p1 = pNext[o][p]; s += score[o][p]; o = o1; p = p1; startO[i] = o; startP[i] = p; startS[i] = s; } } } int worstI = -1; int worstScore = ROUNDS + 1; for (int i = 0; i < opponent.n; i++) { int s = startS[i]; if (s < worstScore) { worstScore = s; worstI = i; } } if (worstScore < 1L * ROUNDS * PERCENTAGE / 100) { return new Outcome(WA, String.format("Wins only %d rounds versus initial state %d", worstScore, worstI + 1)); } return new Outcome(OK, String.format("Epic win in %d rounds (%d states in FSM)", worstScore, player.n)); } static class FSM { int n; int[] move; int[][] next; public FSM(int n) { // used by stress this.n = n; move = new int[n]; next = new int[n][M]; } public FSM(InStream stream, boolean failOnError) { n = stream.nextInt(); if (n < 1 || n > MAX) { throw new Outcome(failOnError ? FAIL : WA, String.format("Invalid number of states: %d", n)); } move = new int[n]; next = new int[n][M]; for (int i = 0; i < n; i++) { String s = stream.nextToken(); if (s.length() != 1 || !MOVES.contains(s)) { throw new Outcome(failOnError ? FAIL : PE, String.format("Invalid move \"%s\" in state %d", s, i + 1)); } move[i] = MOVES.indexOf(s); for (int j = 0; j < M; j++) { next[i][j] = stream.nextInt() - 1; if (next[i][j] < 0 || next[i][j] >= n) { throw new Outcome(failOnError ? FAIL : WA, String.format("Invalid next state %d in state %d", next[i][j] + 1, i + 1)); } } } if (!stream.seekEoF()) throw new Outcome(failOnError ? FAIL : PE, String.format("Extra data in output file")); } } }