import java.util.*; /** * Construct a graph with one node for each letter. Assign a * count to each directed edge with the count corresponding to * how many words there are that start with the source vertex letter * and end with the destination vertex letter. We can simplify by * reducing some counts on the edges. Namely, if there are edges X->Y * and Y->X both with positive multiplicities, we can reduce their * multiplicities by 1 each (or in the case of a self-loop reduce the * multiplicity by 2). * Suppose we are in a losing position. Then the position * with additional edges X->Y, Y->X is also losing, since as soon as * the losing player takes one of those edges, the winning player * can respond by taking the other and the losing player will be back * in a known losing state. * After reducing the graph edge multiplicities we are either left in a * state with a small number of possible moves, or a state with a simple * cycle that can be repeated n times and possibly one self loop on each * node. If the loop repeats at least 3 times, then either player can * force all of the remaining self-loops to be used. So the winner will * be the winner after all remaining edges are used. If there are not * 3 full cycles remaining then there are not many moves left anyway, * so just brute-force the remaining game. * * @author Finn Lidbetter */ public class firstlast_finn { public static void main(String[] arg) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int[][] counts = new int[3][3]; HashMap chToIndex = new HashMap<>(); int currIndex = 0; for (int i=0; i0) { counts[i][j]--; if (!isWinning(counts, j)) { winningMoves += counts[i][j] + 1; } counts[i][j]++; } } } System.out.println(winningMoves); } static boolean isWinning(int[][] counts, int starter) { int[][] reducedCopy = new int[3][3]; for (int i=0; i<3; i++) { for (int j=0; j<3; j++) { reducedCopy[i][j] = counts[i][j]; } } reduce(reducedCopy); int loopsRemaining = reducedLoopsRemaining(reducedCopy); if (loopsRemaining>=3) { // If there are at least 3 loops left, then either player can // force any remaining self-loops to happen. So the winner is // going to be whoever wins if all possible remaining moves // are played. int movesRemaining = 3*loopsRemaining + reducedCopy[0][0] + reducedCopy[1][1] + reducedCopy[2][2] + movesToReachMinLoops(reducedCopy, starter, loopsRemaining); if (movesRemaining%2 == 0) { return false; } return true; } return isWinningBruteForce(reducedCopy, starter); } /** * Count the number of a-b-c-a loops remaining (in either direction) after reducing. */ static int reducedLoopsRemaining(int[][] counts) { if (counts[0][1] > 0 && counts[1][2] > 0 && counts[2][0] > 0) { return Math.min(counts[0][1], Math.min(counts[1][2], counts[2][0])); } if (counts[0][2] > 0 && counts[2][1] > 0 && counts[1][0] > 0) { return Math.min(counts[0][2], Math.min(counts[2][1], counts[1][0])); } return 0; } static int movesToReachMinLoops(int[][] counts, int starter, int minLoops) { if (counts[0][1] > 0) { if (counts[starter][(starter + 1)%3] == minLoops) { return 0; } else if (counts[(starter + 1) %3][(starter + 2) % 3] == minLoops) { return 1; } else { return 2; } } else if (counts[1][0] > 0) { if (counts[starter][(starter-1+3)%3] == minLoops) { return 0; } else if (counts[(starter-1+3) % 3][(starter-2+3)%3] == minLoops) { return 1; } else { return 2; } } return 0; } static int sum(int[][] counts) { int total = 0; for (int i=0; i<3; i++) { for (int j=0; j<3; j++) { total += counts[i][j]; } } return total; } static void reduce(int[][] counts) { for (int i=0; i<3; i++) { counts[i][i] = counts[i][i]%2; } for (int i=0; i<3; i++) { for (int j=0; j 0) { // Try this move. counts[starter][i] -= 1; if (!isWinningBruteForce(counts, i)) { // We forced the opponent to a losing position, so this is winning. counts[starter][i] += 1; return true; } counts[starter][i] += 1; } } // If we reached here, then every legal move puts the // opponent in a winning position, or there are no legal moves. return false; } }