// Floyd-Warshall closure on the graph rather than on the matrix import java.util.*; class Node { public String name; public ArrayList is; public ArrayList has; public Node(String n) { name = n; is = new ArrayList(); has = new ArrayList(); } } public class GraphClosure { public static Scanner in; public static int n,m,count; public static HashMap lookup; public static HashMap checkInit; public static HashMap alreadyChecked; public static void main(String[] args) { in = new Scanner(System.in); n = in.nextInt(); m = in.nextInt(); lookup = new HashMap(); checkInit = new HashMap(); for (int i = 0; i < n; i++) { String left = in.next(); String rel = in.next(); String right = in.next(); Node l = lookup.get(left); if (l==null) { l = new Node(left); lookup.put(left,l); checkInit.put(left,false); } Node r = lookup.get(right); if (r==null) { r = new Node(right); lookup.put(right,r); checkInit.put(right,false); } if (rel.equals("is-a")) { if (! l.is.contains(r)) { l.is.add(r); } } else { if (! l.has.contains(r)) { l.has.add(r); } } } // sort-of close the graph: closeIs(); closeHas(); for (int i = 0; i < m; i++) { String left = in.next(); String rel = in.next(); String right = in.next(); //Node l = lookup.get(left); //Node r = lookup.get(right); System.out.print("Query "+(i+1)+": "); if (rel.equals("is-a")) { System.out.println(processIsA(left,right)?"true":"false"); } else { System.out.println(processHasA(left,right)?"true":"false"); } } // display(); } public static void display() { Iterator it = lookup.values().iterator(); while (it.hasNext()) { Node temp = it.next(); if (!temp.is.isEmpty()) { System.out.print(temp.name+" is-a "); for (Node i: temp.is) { System.out.print(i.name+", "); } System.out.print("\n"); } if (!temp.has.isEmpty()) { System.out.print(temp.name+" has-a "); for (Node i: temp.has) { System.out.print(i.name+", "); } System.out.println(); } } } // To process "X is-a Y", look up node for X and trace all is-a paths // using depth-first search of the is-a graph. // (Could be several paths due to multiple inheritance.) public static boolean processIsA(String left, String right) { Node nl = lookup.get(left); return dfsIsA(nl,right); } public static boolean dfsIsA(Node nl, String target) { // Do a depth-first-search of the "is-a" graph starting at nd: if (nl.name.equals(target)) { return true; } for (Node nr: nl.is) { // if (dfsIsA(nr,target)) { if (nr.name.equals(target)) { return true; } } return false; } public static boolean processHasA(String left, String right) { // A has-a B if: // A has-a C and C has-a B (dfs in the has graph) // A is-a C and C has-a B (dfs in the is graph and, for each one, // dfs in the has graph) // A has-a C and C is-a B (dfs in the has graph and, for each one, // dfs in the is graph) // Make sure we aren't doing a circular search: Node nl = lookup.get(left); //alreadyChecked = (HashMap)checkInit.clone(); return dfsHasA(false,nl,right); } public static boolean dfsHasA(boolean usedHasa, Node nl, String target) { //String left = nl.name; //if (usedHasa && alreadyChecked.get(left)) { // return false; // may need to re-visit a node after first "has-a" edge //} //if (usedHasa) { // don't start counting duplicate visits until we've // // traversed a "has-a" edge // alreadyChecked.put(left,true); //} // Check the node's "has-a" list: for (Node nr: nl.has) { if (nr.name.equals(target)) { return true; } } return false; } /* if (dfsHasA(true,nr,target)) { return true; } } // Check the node's is-a ancestors: for (Node nr: nl.is) { if (usedHasa && nr.name.equals(target)) { return true; } if (dfsHasA(usedHasa,nr,target)) { return true; } } return false; } */ public static void closeIs() { Iterator itk = lookup.values().iterator(); while(itk.hasNext()) { Node k = itk.next(); Iterator iti = lookup.values().iterator(); while (iti.hasNext()) { Node i = iti.next(); Iterator itj = lookup.values().iterator(); while (itj.hasNext()) { Node j = itj.next(); if (i.is.contains(j)) continue; if (i.is.contains(k) && k.is.contains(j)) i.is.add(j); } } } } public static void closeHas() { Iterator itk = lookup.values().iterator(); while(itk.hasNext()) { Node k = itk.next(); Iterator iti = lookup.values().iterator(); while (iti.hasNext()) { Node i = iti.next(); Iterator itj = lookup.values().iterator(); while (itj.hasNext()) { Node j = itj.next(); if (i.has.contains(j)) continue; if (i.has.contains(k) && k.has.contains(j)) { i.has.add(j); continue; } if (i.has.contains(k) && k.is.contains(j)) { i.has.add(j); continue; } if (i.is.contains(k) && k.has.contains(j)) { i.has.add(j); continue; } } } } } }