/* Java solution for Tafeltjes3 for NKP2005 author: P.G.Kluit date : May 2005 */ import java.io.*; import java.util.*; public class ProblemH { private static String infile = "h.in"; public static void main (String [] args){ //Stopwatch watch = new Stopwatch(); try{ if (args.length > 0) // another inputfile can be given infile = args[0]; // as command line argument FileReader fr = new FileReader(infile); BufferedReader br = new BufferedReader(fr); String caseString = br.readLine(); int cases = Integer.parseInt(caseString); for (int geval = 1; geval <= cases; geval++){ String edgeString = br.readLine(); int edges = Integer.parseInt(edgeString); String contour = br.readLine(); TafeltjesProblem p = new TafeltjesProblem(edges, contour.trim()); // watch.start(); p.solve(); //watch.stop(); System.out.println(p.getSolution() + ""); //System.out.println(" duur: " + watch.getDuur()/1000 + " seconden"); } } catch (IOException iox){ System.err.println(iox.toString()); } } } class TafeltjesProblem { private static char NUL = '0'; private Contour contour = new Contour(); int telling; public TafeltjesProblem(int edges, String s){ int count = 0; int index = 0; while (count < edges){ while (index < s.length() && s.charAt(index) == ' ') index++; // skip spaces int x = s.charAt(index) - NUL; index++; int y = s.charAt(index) - NUL; index++; int z = s.charAt(index) - NUL; index++; Triple hoek = new Triple(x,y,z); contour.add(hoek); //System.out.println(hoek); count++; } contour.close(); //System.out.println(contour); } public void solve(){ boolean [] triangles = contour.solve(); Vorm vorm = new Vorm(triangles); telling = vorm.count(); } public String getSolution(){ return "" + telling; } } class Triple{ int x,y,z; public Triple(int x, int y, int z){ this.x = x; this.y = y; this.z = z; } public int getCode(){ int base = (x + y)/3; int antwoord = base * base + 2 * (x + 1) / 3; //System.out.print( " -" + antwoord + "- "); return antwoord; } public Triple plus(Triple other){ return new Triple(this.x + other.x, this.y + other.y, this.z + other.z); } public Triple minus(Triple other){ return new Triple(this.x - other.x, this.y - other.y, this.z - other.z); } public Triple rotate(){ return new Triple(z,x,y); } public boolean equals (Object o){ Triple other = (Triple) o; return this.x == other.x && this.y == other.y && this.z == other.z; } public boolean lessThan(Triple that){ return this.x < that.x || (this.x == that.x && (this.y < that.y || (this.y == that.y && this.z < that.z))); } public String toString(){ return "(" + x + ", " + y + ", " + z + ")"; } public void reduce(){ int d = 2; while ( d <= Math.max(x,Math.max(y,z))){ while (x % d == 0 && y % d == 0 && z % d == 0){ x = x / d; y = y / d; z = z / d; } d++; } } } class Edge{ Triple start, end; public Edge(Triple s, Triple e){ start = s; end = e; } public Triple direction(){ return end.minus(start); } public String toString(){ return "[" + start.toString() + " - " + end.toString() + "]"; } public boolean isInvers(Edge that){ return this.start.equals(that.end) && that.start.equals(this.end); } } class Contour{ Vector edges = new Vector(); Triple lastTriple; //private int [] triangles = new int [100]; //int trianglenum = 0; boolean [] driehoek = new boolean [82]; public void add (Triple hoek){ if (lastTriple == null) lastTriple = hoek; else{ Triple step = hoek.minus(lastTriple); step.reduce(); Triple next = lastTriple; do{ next = next.plus(step); edges.add( new Edge(lastTriple, next)); lastTriple = next; } while (! next.equals(hoek)); } } public boolean [] solve(){ while (!edges.isEmpty()){ Edge lastEdge = (Edge)edges.lastElement(); Triple dir = lastEdge.direction(); Triple newDir = dir.rotate(); Triple eerste = lastEdge.start; Triple tweede = lastEdge.end; Triple derde = tweede.plus(newDir); Triple drieh = eerste.plus(tweede.plus(derde)); //System.out.println(drieh); int code = drieh.getCode(); driehoek[code] = true; edges.remove (lastEdge); addEdge(new Edge (eerste, derde)); addEdge(new Edge (derde, tweede)); } return driehoek; } public void close(){ Triple firstTriple = getEdge(0).start; add (firstTriple); } public String toString(){ String antwoord = ""; for (int k = 0; k < edges.size(); k++) antwoord += getEdge(k).toString() + "\n"; return antwoord; } private void insert(int t){ driehoek[t] = true; } private Edge getEdge(int k){ return (Edge) edges.elementAt(k); } private void addEdge(Edge e){ int k = 0; while (k < edges.size() &&! getEdge(k).isInvers(e)) k++; if (k < edges.size()) edges.remove(k); else edges.add(e); } } class PreVorm{ static boolean incident [] [] = new boolean [82][250]; static { for (int d = 1; d <= 81; d++){ int onder, boven, derde; onder = onderbuur(d); boven = bovenbuur(d); derde = derdebuur(d); int tafeltje; tafeltje = 3 * d; if (onder > 0 && boven > 0){ incident[d][tafeltje] = true; incident[onder][tafeltje] = true; incident[boven][tafeltje] = true; } tafeltje++; if (derde > 0 && boven > 0){ incident[d][tafeltje] = true; incident[derde][tafeltje] = true; incident[boven][tafeltje] = true; } tafeltje ++; if (onder > 0 && derde > 0){ incident[d][tafeltje] = true; incident[onder][tafeltje] = true; incident[derde][tafeltje] = true; } } //dump(); } static int onderbuur(int d){ if (isSquare(d -1)) return -1; else return d - 1; } static int bovenbuur(int d){ if (isSquare(d)) return -1; else return d + 1; } static int derdebuur(int d){ int s = superSquare (d); int derde; if ( (d - s ) % 2 == 0) derde = d + 2 * s; else derde = d - 2 * ( s-1); if (derde <= 81) return derde; else return -1; } static boolean isSquare(int d){ int s = superSquare (d); return d == s * s; } static int superSquare(int d){ int s = (int)Math.sqrt(d-1); while (s * s < d) s++; return s; } static void dump(){ for (int k = 1; k <= 81; k++){ System.out.print ( k + " "); int c = 0; for (int t = 3; t <= 245; t++) if (incident[k][t]) { System.out.print("+"); c++; } else System.out.print(" "); System.out.println( " " + c); } } } class Vorm extends PreVorm{ private boolean [] driehoek; private boolean [] tafeltje; private int size = 0; public Vorm (boolean [] dh){ driehoek = dh; for (int d = 1; d <= 81; d++) if (driehoek[d]) size++; tafeltje = new boolean [243]; for (int t = 0; t < 243; t++) tafeltje[t] = true; } public Vorm (boolean [] dh, boolean [] tf){ driehoek = dh; tafeltje = tf; for (int d = 1; d <= 81; d++) if (driehoek[d]) size++; } public int count(){ if (size % 3 > 0) return 0; if (size == 0) return 1; for (int tafel = 0; tafel < 243; tafel++) if (tafeltje[tafel]){ int d = 1; while (d <= 81 && (driehoek [d] || !incident [d][tafel])) d++; if (d <= 81) tafeltje [tafel] = false; } int champval = 10; int champ = -1; for (int d = 1; d <= 81; d++) if (driehoek[d]){ int telling = 0; for (int t = 0; t < 243; t++) if (incident[d][t] && tafeltje [t]) telling++; if (driehoek[d] && telling < champval){ champ = d; champval = telling; } } // champ is de driehoek die we gaan bedekken // want die heeft de minste keuzemogelijkheden. int antwoord = 0; for (int t = 0; t < 243; t++) if ( incident[champ][t] && tafeltje[t]){ boolean [] kopie = (boolean []) driehoek.clone(); // driehoeken schrappen for (int d = 1; d <= 81; d++) if (incident[d][t]) kopie[d] = false; Vorm vorm = new Vorm (kopie, (boolean []) tafeltje.clone()); antwoord += vorm.count(); } return antwoord; } private void consistent(){ for (int t = 0; t < 243; t++) if (tafeltje[t]){ int tel = 0; for (int d = 1; d <= 81; d++) if (incident[d][t] && driehoek[d]) tel++; if (tel % 3 > 0) System.out.println("inconsistent " + t + " " + tel); } } public String toString(){ String antwoord = ""; for (int k = 1; k <= 81; k++) if (driehoek[k]) antwoord += "1"; else antwoord += "-"; return antwoord+ " size = " + size ; } }