Problem: Note that the only difference between the easy and hard version is the constraint on the number of queries. You can make hacks only if all versions of the problem are solved.

This is an interactive problem.

You are given a tree consisting of n nodes numbered with integers from 1 to n. Ayush and Ashish chose two secret distinct nodes in the tree. You need to find out both the nodes. You can make the following query: 

  * Provide a list of nodes and you will receive a node from that list whose sum of distances to both the hidden nodes is minimal (if there are multiple such nodes in the list, you will receive any one of them). You will also get the sum of distances of that node to the hidden nodes. 



Recall that a tree is a connected graph without cycles. The distance between two nodes is defined as the number of edges in the simple path between them.

More formally, let's define two hidden nodes as s and f. In one query you can provide the set of nodes \\{a_1, a_2, …, a_c\} of the tree. As a result, you will get two numbers a_i and dist(a_i, s) + dist(a_i, f). The node a_i is any node from the provided set, for which the number dist(a_i, s) + dist(a_i, f) is minimal.

You can ask no more than 14 queries.

Input

The first line contains a single integer t (1 ≤ t ≤ 10) — the number of test cases. Please note, how the interaction process is organized.

The first line of each test case consists of a single integer n (2 ≤ n ≤ 1000) — the number of nodes in the tree.

The next n - 1 lines consist of two integers u, v (1 ≤ u, v ≤ n, u ≠ v) — the edges of the tree.

Interaction

To ask a query print a single line: 

  * In the beginning print "? c " (without quotes) where c (1 ≤ c ≤ n) denotes the number of nodes being queried, followed by c distinct integers in the range [1, n] — the indices of nodes from the list. 



For each query, you will receive two integers x, d — the node (among the queried nodes) with the minimum sum of distances to the hidden nodes and the sum of distances from that node to the hidden nodes. If the subset of nodes queried is invalid or you exceeded the number of queries then you will get x = d = -1. In this case, you should terminate the program immediately.

When you have guessed the hidden nodes, print a single line "! " (without quotes), followed by two integers in the range [1, n] — the hidden nodes. You can output the hidden nodes in any order.

After this, you should read a string. If you guess the nodes correctly, you will receive the string "Correct". In this case, you should continue solving the remaining test cases or terminate the program, if all test cases were solved. Otherwise, you will receive the string "Incorrect". In this case, you should terminate the program immediately.

Guessing the hidden nodes does not count towards the number of queries asked.

The interactor is not adaptive. The hidden nodes do not change with queries.

Do not forget to read the string "Correct" / "Incorrect" after guessing the hidden nodes.

You need to solve each test case before receiving the input for the next test case.

The limit of 14 queries applies to each test case and not to the entire input.

After printing a query do not forget to output the end of the line and flush the output. Otherwise, you will get Idleness limit exceeded. To do this, use:

  * fflush(stdout) or cout.flush() in C++;
  * System.out.flush() in Java;
  * flush(output) in Pascal;
  * stdout.flush() in Python;
  * see the documentation for other languages.



Hacks

To hack the solution, use the following test format:

The first line should contain a single integer t (1 ≤ t ≤ 10) — the number of test cases. The description of the test cases follows.

The first line of each test case should contain a single integer n (2 ≤ n ≤ 1000) — the number of nodes in the tree. The second line should contain two distinct integers in the range [1, n] — the hidden nodes. The next n - 1 lines should contain two integers u, v (1 ≤ u, v ≤ n, u ≠ v) — the edges of the tree.

Example

Input


1
3
1 2
1 3

1 1

2 3

3 1

3 1

Correct

Output


? 1 1

? 1 2

? 1 3

? 2 2 3

! 1 3

Note

The tree from the first test is shown below, and the hidden nodes are 1 and 3.

<image>

Solution (in JAVA):

Solution:
//package cf646;

import java.io.*;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

// What's the smallest input?
// Check * for int/long overflow.
// Check / for accidental rounding.
// Are you using doubles? Can you avoid it?
// Never compare after taking mod.
// Mod the final result.
// Initialize globals in solve() unless they are independent of the problem input.
// Check for local/global name conflicts (n?).
// Check initial values for max/min (not Integer.MIN_VALUE for long).
public class D {
    static Random rand;
    static boolean MULTIPLE_CASES = true;
    static boolean CONSTRUCTIVE = false;
    static long MOD = (long) 1e9 + 7;
    static long BIG = (long) 2e9 + 10;

    static class Maximum {
        int ind, val;

        public Maximum(int ind, int val) {
            this.ind = ind;
            this.val = val;
        }
    }

    public static void solve(Reader in, PrintWriter out) {
        int n = in.ni(), k = in.ni();
        Set<Integer>[] subsets = new Set[k];
        for (int i = 0; i < k; i++) {
            int size = in.ni();
            subsets[i] = new HashSet<>(size);
            for (int j = 0; j < size; j++) subsets[i].add(in.ni());
        }

        Maximum max = getMaximum(n, in, out);
        int[] ans = new int[k];
        for (int i = 0; i < k; i++) {
            if (subsets[i].contains(max.ind)) {
                ans[i] = querySubset(allBut(subsets[i], n), in, out);
            } else {
                ans[i] = max.val;
            }
        }

        out.print("! ");
        for (int i : ans) {
            out.print(i + " ");
        }
        out.println();
        out.flush();
        String result = in.next();
    }

    static Set<Integer> allBut(Set<Integer> set, int n) {
        Set<Integer> out = new HashSet<>();
        for (int i = 1; i <= n; i++) {
            if (!set.contains(i)) out.add(i);
        }
        return out;
    }

    static Maximum getMaximum(int n, Reader in, PrintWriter out) {
        int min = 1;
        int max = n;
        int mid = (min + max) / 2;
        int cand1 = queryRange(min, mid, in, out);
        int cand2 = queryRange(mid+1, max, in, out);
        int maxElt;
        if (cand1 > cand2) {
            max = mid;
            maxElt = cand1;
        } else {
            min = mid+1;
            maxElt = cand2;
        }

        while (min < max) {
            mid = (min + max) / 2;
            if (queryRange(min, mid, in, out) == maxElt) {
                max = mid;
            } else {
                min = mid + 1;
            }
        }
        return new Maximum(min, maxElt);
    }

    // incl
    static int queryRange(int l, int r, Reader in, PrintWriter out) {
        out.print("? " + (r - l + 1) + " ");
        for (int i = l; i <= r; i++) {
            out.print(i + " ");
        }
        out.println();
        out.flush();
        return in.ni();
    }

    static int querySubset(Set<Integer> set, Reader in, PrintWriter out) {
        out.print("? " + set.size() + " ");
        for (int i : set) out.print(i + " ");
        out.println();
        out.flush();
        return in.ni();
    }

    public static void main(String[] args) throws IOException {
        Reader in = null;
        PrintWriter out = null;
        for (String arg : args) {
            if (arg.startsWith("input")) {
                in = new Reader(arg);
            } else if (arg.startsWith("output")) {
                out = new PrintWriter(new FileWriter(arg));
            }
        }
        if (in == null) in = new Reader();
        if (out == null) out = new PrintWriter(new OutputStreamWriter(System.out));

        if (MULTIPLE_CASES) {
            int tests = in.ni();
            for (int t = 0; t < tests; t++) {
                solve(in, out);
            }
        } else {
            solve(in, out);
        }

        out.flush();
    }

    static class Tester {
        public static void main(String[] args) throws IOException {
            Validator validator = new Validator();

            File currentDir = new File(".");
            long before;
            long after;
            for (File inputFile : Arrays.stream(currentDir.listFiles())
                    .sorted(Comparator.comparing(File::getName))
                    .collect(Collectors.toList())) {
                if (!inputFile.getName().startsWith("input")
                        || inputFile.getName().contains("sight")
                        || inputFile.getName().contains("big")) continue;
                System.out.println("Test file: " + inputFile.getName());

                before = System.nanoTime();
                String outputFileName = "output" + inputFile.getName().substring(5);
                D.main(new String[]{inputFile.getName(), outputFileName});
                after = System.nanoTime();

                File outputFile = new File(outputFileName);
                if (!outputFile.exists()) {
                    throw new IllegalStateException("Missing output file " + outputFile);
                }

                // TODO if verifier is implemented, remove this
                if (CONSTRUCTIVE) {
                    System.out.println("INPUT:");
                    printFile(inputFile);
                    System.out.printf("\nOUTPUT: (%d milliseconds)\n", (after - before) / 1000000L);
                    printFile(outputFile);
                    continue;
                }

                if (validator.validate(inputFile, outputFile)) {
                    System.out.printf("OK (%d milliseconds)\n", (after - before) / 1000000L);
                } else {
                    System.out.println("FAILED");
                    System.out.println("\nInput: ");
                    printFile(inputFile);
                    System.out.println("\nIncorrect Output: ");
                    printFile(outputFile);
                    return;
                }
            }
            System.out.println("\n-----------------\n");

            File sightTestInput = new File("input-sight");
            if (sightTestInput.exists()) {
                System.out.println("Running sight test... Input:");
                printFile(sightTestInput);
                before = System.nanoTime();
                System.out.println("\nOutput: ");
                D.main(new String[]{"input-sight"});
                after = System.nanoTime();
                System.out.printf("(%d milliseconds)\n", (after - before) / 1000000L);
                System.out.println("\n-----------------\n");
            }

            File bigTestInput = new File("input-big");
            if (bigTestInput.exists()) {
                System.out.println("Running big test...");
                before = System.nanoTime();
                D.main(new String[]{"input-big"});
                after = System.nanoTime();
                System.out.printf("(%d milliseconds)\n", (after - before) / 1000000L);
            }
        }

        static void printFile(File file) throws FileNotFoundException {
            BufferedReader reader = new BufferedReader(new FileReader(file));
            reader.lines().forEach(System.out::println);
        }
    }

    static class Validator {
        boolean validate(File inputFile, File outputFile) throws IOException {
            File expectedOutputFile = new File("expected-output" + inputFile.getName().substring(5));
            if (CONSTRUCTIVE || !expectedOutputFile.exists()) {
                return validateManual(inputFile, outputFile);
            }
            return areSame(outputFile, expectedOutputFile);
        }

        private boolean validateManual(File inputFile, File outputFile) throws IOException {
            if (CONSTRUCTIVE) {
                // Validate output against input
                Reader inputReader = new Reader(inputFile.getName());
                Reader outputReader = new Reader(outputFile.getName());

                // TODO implement manual validation

                throw new IllegalStateException("Missing expected output file");
            } else {
                File naiveOutput = new File("naive-" + outputFile.getName());
                Naive.main(new String[]{inputFile.getName(), naiveOutput.getName()});

                return areSame(outputFile, naiveOutput);
            }
        }

        private boolean areSame(File file1, File file2) throws IOException {
            BufferedReader reader1 = new BufferedReader(new FileReader(file1));
            BufferedReader reader2 = new BufferedReader(new FileReader(file2));

            String line1;
            while ((line1 = reader1.readLine()) != null) {
                String line2 = reader2.readLine();
                if (line2 == null) line2 = ""; // ok if one has an extra newline
                if (!line1.trim().equals(line2.trim())) {
                    return false;
                }
            }
            String line2;
            while ((line2 = reader2.readLine()) != null) {
                if (!line2.trim().isEmpty()) {
                    return false;
                }
            }
            return true;
        }

    }

    static class Naive {
        public static void solveNaive(Reader in, PrintWriter out) {
            throw new IllegalStateException("missing expected output file");
        }

        public static void main(String[] args) throws IOException {
            Reader in = null;
            PrintWriter out = null;
            for (String arg : args) {
                if (arg.startsWith("input")) {
                    in = new Reader(arg);
                } else if (arg.startsWith("naive-output")) {
                    out = new PrintWriter(new FileWriter(arg));
                }
            }
            if (in == null) in = new Reader();
            if (out == null) out = new PrintWriter(new OutputStreamWriter(System.out));

            int tests = in.ni();
            for (int t = 0; t < tests; t++) {
                solveNaive(in, out);
            }

            out.flush();
            out.close();
        }
    }

    static boolean isPowerOfTwo(int x) {
        return x > 0 & (x & (x - 1)) == 0;
    }

    static int gcd(int a, int b) {
        return b == 0 ? a : gcd(b, a % b);
    }

    static void ruffleSort(int[] a) {
        int n = a.length;
        for (int i = 0; i < n; i++) {
            int oi = rand.nextInt(n), temp = a[oi];
            a[oi] = a[i];
            a[i] = temp;
        }
        Arrays.sort(a);
    }

    static void ruffleSort(long[] a) {
        int n = a.length;
        for (int i = 0; i < n; i++) {
            int oi = rand.nextInt(n);
            long temp = a[oi];
            a[oi] = a[i];
            a[i] = temp;
        }
        Arrays.sort(a);
    }

    ///////////////////////////////////
    //////////   FAST PAIR   //////////
    ///////////////////////////////////

    // Works on -1e9 <= x, y <= 1e9
    static long pair(int x, int y) {
        x = (int) BIG / 2 - x;
        y = (int) BIG / 2 - y;
        return x * BIG + y;
    }

    static int x(long pair) {
        return (int) BIG / 2 - (int) (pair / BIG);
    }

    static int y(long pair) {
        return (int) BIG / 2 - (int) (pair % BIG);
    }

    static String str(long pair) {
        return String.format("(%d, %d)", x(pair), y(pair));
    }

    ///////////////////////////////////
    ////////// BINARY SEARCH //////////
    ///////////////////////////////////

    // return highest in range that still returns true
    // T T T F F F F
    // invariant:
    // -    indicator(min) = true
    // -    indicator(max+1) = false
    static int binarySearchHighest(int min, int max, Function<Integer, Boolean> indicator) {
        int a = min;
        int b = max;
        while (a != b) {
            int mid = (a % 2 != b % 2) ? 1 + (a + b) / 2 : (a + b) / 2;
            if (indicator.apply(mid)) {
                a = mid;
            } else {
                b = mid - 1;
            }
        }
        return a;
    }

    // return lowest in range that still returns true
    // F F F F T T T
    // invariant:
    // -    indicator(min-1) = false
    // -    indicator(max) = true
    static int binarySearchLowest(int min, int max, Function<Integer, Boolean> indicator) {
        int a = min;
        int b = max;
        while (a != b) {
            int mid = (a + b) / 2;
            if (indicator.apply(mid)) {
                b = mid;
            } else {
                a = mid + 1;
            }
        }
        return a;
    }

    static void insist(boolean bool) {
        if (!bool) throw new IllegalStateException();
    }

    static class Reader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public Reader() {
            reader = new BufferedReader(new InputStreamReader(System.in), 32768);
            tokenizer = null;
        }

        public Reader(String fileName) throws FileNotFoundException {
            reader = new BufferedReader(new FileReader(fileName));
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public int ni() {
            return Integer.parseInt(next());
        }

        public long nl() {
            return Long.parseLong(next());
        }

        public int[] nis(int n) {
            int[] out = new int[n];
            for (int i = 0; i < n; i++) {
                out[i] = ni();
            }
            return out;
        }

        public long[] nls(int n) {
            long[] out = new long[n];
            for (int i = 0; i < n; i++) {
                out[i] = nl();
            }
            return out;
        }
    }
}