Problem: cf1061_f
Difficulty: easy
Categories: graph, data structures, math
Time limit: 3000 ms, memory limit: 256 MB

The graph is called tree if it is connected and has no cycles. Suppose the tree is rooted at some vertex. Then tree is called to be perfect $k$-ary tree if each vertex is either a leaf (has no children) or has exactly $k$ children. Also, in perfect $k$-ary tree all leafs must have same depth.

For example, the picture below illustrates perfect binary tree with $15$ vertices:

![](https://espresso.codeforces.com/469a96d5903beaaabeb929d8ba57ebf7a548b61d.png)

There is a perfect $k$-ary tree with $n$ nodes. The nodes are labeled with distinct integers from $1$ to $n$, however you don't know how nodes are labelled. Still, you want to find the label of the root of the tree.

You are allowed to make at most $60 \cdot n$ queries of the following type:

* "? $a$ $b$ $c$", the query returns "Yes" if node with label $b$ lies on the path from $a$ to $c$ and "No" otherwise.

Both $a$ and $c$ are considered to be lying on the path from $a$ to $c$.

When you are ready to report the root of the tree, print

* "! $s$", where $s$ is the label of the root of the tree.

It is possible to report the root only once and this query is not counted towards limit of $60 \cdot n$ queries.

Interaction

The first line of the standard input stream contains two integers $n$ and $k$ ($3 \le n \le 1500$, $2 \le k < n$) — the number of nodes in the tree and the value of $k$.

It is guaranteed that $n$ is such that the tree forms a perfect $k$-ary tree.

You can ask at most $60 \cdot n$ queries. To ask a query, print a line of form "? $a$ $b$ $c$", where $1 \le a, b, c \le n$. After that you should read a single line containing "Yes" or "No" depending on the answer of the query.

The tree is fixed for each test and it doesn't depend on your queries.

When you are ready to print the answer, print a line of the form "! $s$", where $s$ is the label of the root vertex and then terminate your program.

After printing each query do not forget to print end of line and flush the output. Otherwise you may 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 documentation for other languages.

In case your program will make more than $60 \cdot n$ queries, but in other aspects would follow the interaction protocol and terminate coorectly, it will get verdict «Wrong Answer».

Example

Input

```
3 2  
  
No  
  
Yes
```

Output

```
? 1 3 2  
  
? 1 2 3  
  
! 2
```

Note

The tree in the example is as follows:

![](https://espresso.codeforces.com/855f62ae639f7f71a390a75d5202929cafd40070.png)

The input and output for example illustrate possible interaction on that test (empty lines are inserted only for clarity).

Non-adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>
using namespace std;

static long long queries = 0;
static long long query_limit = 0;

static void log_metrics() {
    try {
        tout << "queries=" << queries << "\n";
        tout << "query_limit=" << query_limit << "\n";
        tout.flush();
    } catch (...) {
        
    }
}

static void finish(TResult verdict, const string& msg) {
    log_metrics();
    quitf(verdict, "%s", msg.c_str());
}

static bool isDigits(const string& s) {
    if (s.empty()) return false;
    for (char ch : s) if (ch < '0' || ch > '9') return false;
    return true;
}

static int readIntTokenInRange(int n, const char* what) {
    string tok;
    if (!(cin >> tok)) finish(_pe, string("Unexpected EOF while reading ") + what);
    if (!isDigits(tok)) finish(_pe, string("Non-integer token for ") + what);
    long long v = 0;
    try {
        v = stoll(tok);
    } catch (...) {
        finish(_pe, string("Bad integer token for ") + what);
    }
    if (v < 1 || v > n) finish(_pe, string("Out of range value for ") + what);
    return (int)v;
}

struct LCA {
    int n = 0, LOG = 0;
    vector<int> depth;
    vector<vector<int>> up;

    LCA() {}
    LCA(int n_, const vector<int>& parent, const vector<int>& depth_) { init(n_, parent, depth_); }

    void init(int n_, const vector<int>& parent, const vector<int>& depth_) {
        n = n_;
        depth = depth_;
        LOG = 1;
        while ((1 << LOG) <= n) LOG++;
        up.assign(LOG, vector<int>(n + 1, 0));
        for (int v = 1; v <= n; v++) up[0][v] = parent[v];
        for (int j = 1; j < LOG; j++) {
            for (int v = 1; v <= n; v++) {
                int mid = up[j - 1][v];
                up[j][v] = (mid == 0 ? 0 : up[j - 1][mid]);
            }
        }
    }

    int lift(int v, int dist) const {
        for (int j = 0; j < LOG; j++) {
            if (dist & (1 << j)) v = up[j][v];
            if (v == 0) break;
        }
        return v;
    }

    int lca(int a, int b) const {
        if (a == 0 || b == 0) return 0;
        if (depth[a] < depth[b]) swap(a, b);
        a = lift(a, depth[a] - depth[b]);
        if (a == b) return a;
        for (int j = LOG - 1; j >= 0; j--) {
            if (up[j][a] != up[j][b]) {
                a = up[j][a];
                b = up[j][b];
            }
        }
        return up[0][a];
    }

    int dist(int a, int b) const {
        int c = lca(a, b);
        if (c == 0) return 0;
        return depth[a] + depth[b] - 2 * depth[c];
    }
};

int main(int argc, char** argv) {
    registerInteraction(argc, argv);
    atexit(log_metrics);

    int n = inf.readInt(3, 1500, "n");
    int k = inf.readInt(2, n - 1, "k");

    vector<int> perm(n + 1, 0);
    bool hasPerm = true;
    for (int i = 1; i <= n; i++) {
        if (inf.eof()) { hasPerm = false; break; }
        perm[i] = inf.readInt(1, n, "perm[i]");
    }
    if (!hasPerm) {
        for (int i = 1; i <= n; i++) perm[i] = i;
    }

    vector<int> inv(n + 1, 0);
    vector<int> seen(n + 1, 0);
    for (int v = 1; v <= n; v++) {
        int lab = perm[v];
        if (lab < 1 || lab > n) finish(_pe, "Hidden permutation out of range");
        if (seen[lab]) finish(_pe, "Hidden permutation not a permutation");
        seen[lab] = 1;
        inv[lab] = v;
    }

    vector<int> parent(n + 1, 0), depth(n + 1, 0);
    parent[1] = 0;
    depth[1] = 0;
    for (int v = 2; v <= n; v++) {
        parent[v] = (v - 2) / k + 1;
        depth[v] = depth[parent[v]] + 1;
    }
    LCA lca(n, parent, depth);

    int rootLabel = perm[1];

    query_limit = 60LL * n;
    const long long hard_cap = max(200000LL, 10LL * query_limit);

    log_metrics(); 

    cout << n << " " << k << "\n";
    cout.flush();

    auto on_path = [&](int aLab, int bLab, int cLab) -> bool {
        int a = inv[aLab];
        int b = inv[bLab];
        int c = inv[cLab];
        int dab = lca.dist(a, b);
        int dbc = lca.dist(b, c);
        int dac = lca.dist(a, c);
        return dac == dab + dbc;
    };

    while (true) {
        string cmd;
        if (!(cin >> cmd)) finish(_pe, "Unexpected EOF (no final answer)");

        if (cmd == "?") {
            int a = readIntTokenInRange(n, "a");
            int b = readIntTokenInRange(n, "b");
            int c = readIntTokenInRange(n, "c");

            queries++;
            if (queries > hard_cap) finish(_pe, "Hard cap exceeded");

            bool ans = on_path(a, b, c);
            cout << (ans ? "Yes" : "No") << "\n";
            cout.flush();
        } else if (cmd == "!") {
            int s = readIntTokenInRange(n, "s");
            if (s == rootLabel) finish(_ok, "Correct");
            finish(_wa, "Wrong root");
        } else {
            finish(_pe, "Invalid command token");
        }
    }
}


Adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>
using namespace std;

static long long queries = 0;
static long long query_limit = 0;

static void log_metrics() {
    try {
        tout << "queries=" << queries << "\n";
        tout << "query_limit=" << query_limit << "\n";
        tout.flush();
    } catch (...) {
        
    }
}

static void finish(TResult verdict, const string& msg) {
    log_metrics();
    quitf(verdict, "%s", msg.c_str());
}

struct Tree {
    int n = 0, k = 0;
    int LOG = 0;
    vector<int> parent, depth;
    vector<vector<int>> up;

    void build(int n_, int k_) {
        n = n_;
        k = k_;
        parent.assign(n + 1, 0);
        depth.assign(n + 1, 0);

        for (int v = 2; v <= n; v++) parent[v] = (v - 2) / k + 1;
        depth[1] = 0;
        for (int v = 2; v <= n; v++) depth[v] = depth[parent[v]] + 1;

        LOG = 1;
        while ((1 << LOG) <= n) LOG++;
        up.assign(LOG, vector<int>(n + 1, 0));
        for (int v = 1; v <= n; v++) up[0][v] = parent[v];
        for (int j = 1; j < LOG; j++) {
            for (int v = 1; v <= n; v++) {
                int mid = up[j - 1][v];
                up[j][v] = (mid ? up[j - 1][mid] : 0);
            }
        }
    }

    int lca(int a, int b) const {
        if (a == 0 || b == 0) return a ^ b;
        if (depth[a] < depth[b]) swap(a, b);
        int diff = depth[a] - depth[b];
        for (int j = 0; j < LOG; j++) if (diff & (1 << j)) a = up[j][a];
        if (a == b) return a;
        for (int j = LOG - 1; j >= 0; j--) {
            if (up[j][a] != up[j][b]) {
                a = up[j][a];
                b = up[j][b];
            }
        }
        return parent[a];
    }

    int dist(int a, int b) const {
        int c = lca(a, b);
        return depth[a] + depth[b] - 2 * depth[c];
    }

    bool onPath(int a, int b, int c) const {
        
        return dist(a, c) == dist(a, b) + dist(b, c);
    }
};

struct AdaptiveLabeling {
    int n = 0;
    Tree* tr = nullptr;

    vector<int> labelToNode; 
    vector<int> nodeToLabel; 
    vector<int> assignOrderNodes;
    int ptr = 0;

    void init(int n_, Tree* tr_) {
        n = n_;
        tr = tr_;
        labelToNode.assign(n + 1, 0);
        nodeToLabel.assign(n + 1, 0);
        ptr = 0;

        
        vector<int> nodes;
        nodes.reserve(n);
        for (int v = 2; v <= n; v++) nodes.push_back(v);
        sort(nodes.begin(), nodes.end(), [&](int a, int b) {
            if (tr->depth[a] != tr->depth[b]) return tr->depth[a] > tr->depth[b];
            return a > b;
        });
        assignOrderNodes = nodes;
        assignOrderNodes.push_back(1);
    }

    int nextUnusedNode() {
        while (ptr < (int)assignOrderNodes.size() && nodeToLabel[assignOrderNodes[ptr]] != 0) ptr++;
        if (ptr >= (int)assignOrderNodes.size()) return 0;
        int v = assignOrderNodes[ptr++];
        if (nodeToLabel[v] != 0) return nextUnusedNode();
        return v;
    }

    void ensureAssigned(int label) {
        if (label < 1 || label > n) finish(_pe, "label out of range");
        if (labelToNode[label] != 0) return;
        int v = nextUnusedNode();
        if (v == 0) finish(_pe, "no unused nodes left for assignment");
        labelToNode[label] = v;
        nodeToLabel[v] = label;
    }

    void completeAllLabelsDeterministically() {
        for (int lab = 1; lab <= n; lab++) ensureAssigned(lab);
        
        for (int v = 1; v <= n; v++) {
            if (nodeToLabel[v] == 0) finish(_pe, "completion failed (unused node remains)");
        }
    }

    int rootLabel() const {
        return nodeToLabel[1];
    }
};

static bool readLongLongSafe(long long& out) {
    string s;
    if (!(cin >> s)) return false;
    int i = 0;
    bool neg = false;
    if (!s.empty() && (s[0] == '+' || s[0] == '-')) {
        neg = (s[0] == '-');
        i = 1;
    }
    if (i == (int)s.size()) return false;
    long long val = 0;
    for (; i < (int)s.size(); i++) {
        if (s[i] < '0' || s[i] > '9') return false;
        int d = s[i] - '0';
        if (val > (LLONG_MAX - d) / 10) return false;
        val = val * 10 + d;
    }
    out = neg ? -val : val;
    return true;
}

int main(int argc, char** argv) {
    registerInteraction(argc, argv);
    atexit(log_metrics);

    int n = 0, k = 0;
    try {
        n = inf.readInt();
        k = inf.readInt();
    } catch (...) {
        finish(_pe, "failed to read n,k from inf");
    }
    if (n < 3 || n > 1500) finish(_pe, "n out of range");
    if (k < 2 || k >= n) finish(_pe, "k out of range");

    query_limit = 60LL * n;
    log_metrics(); 

    long long hard_cap = max(200000LL, 10LL * query_limit);

    Tree tr;
    tr.build(n, k);

    AdaptiveLabeling labeling;
    labeling.init(n, &tr);

    cout << n << " " << k << "\n";
    cout.flush();

    while (true) {
        string tok;
        if (!(cin >> tok)) finish(_pe, "unexpected EOF (no final answer)");

        if (tok == "?") {
            long long aLL, bLL, cLL;
            if (!readLongLongSafe(aLL) || !readLongLongSafe(bLL) || !readLongLongSafe(cLL)) {
                finish(_pe, "malformed query arguments");
            }
            if (aLL < 1 || aLL > n || bLL < 1 || bLL > n || cLL < 1 || cLL > n) {
                finish(_pe, "query label out of range");
            }
            int a = (int)aLL, b = (int)bLL, c = (int)cLL;

            queries++;
            if (queries > hard_cap) finish(_pe, "too many queries (hard cap exceeded)");

            labeling.ensureAssigned(a);
            labeling.ensureAssigned(b);
            labeling.ensureAssigned(c);

            int na = labeling.labelToNode[a];
            int nb = labeling.labelToNode[b];
            int nc = labeling.labelToNode[c];

            bool ans = tr.onPath(na, nb, nc);

            cout << (ans ? "Yes" : "No") << "\n";
            cout.flush();
            continue;
        }

        if (tok == "!") {
            long long sLL;
            if (!readLongLongSafe(sLL)) finish(_pe, "malformed final answer");
            if (sLL < 1 || sLL > n) finish(_pe, "final answer out of range");
            int s = (int)sLL;

            labeling.ensureAssigned(s);
            labeling.completeAllLabelsDeterministically();

            int r = labeling.rootLabel();
            if (r == 0) finish(_pe, "root label not assigned after completion");

            if (s == r) finish(_ok, "correct");
            finish(_wa, "wrong root");
        }

        finish(_pe, "unknown command token");
    }
}


Test generator (C++):
#include "testlib.h"
#include <bits/stdc++.h>
using namespace std;

struct Cand {
    int n;
    int k;
    int h;
    long long score;
};

static int computeHeightIfPerfect(int n, int k) {
    long long total = 1;
    long long level = 1;
    int h = 0;
    while (total < n) {
        if (level > (LLONG_MAX / k)) return -1;
        level *= k;
        total += level;
        h++;
        if (h > 60) return -1;
    }
    if (total != n) return -1;
    return h;
}

static vector<Cand> allCandidatesUpTo(int maxn) {
    vector<Cand> cands;
    for (int k = 2; k <= maxn - 1; k++) {
        long long total = 1;
        long long level = 1;
        int h = 0;
        while (true) {
            if (total >= 3 && total <= maxn) {
                int n = (int)total;
                if (k < n) {
                    long long score = 1000000LL * n + 1000LL * h - k;
                    cands.push_back({n, k, h, score});
                }
            }
            if (total > maxn) break;
            if (level > (LLONG_MAX / k)) break;
            level *= k;
            total += level;
            h++;
            if (total > maxn) break;
            if (h > 60) break;
        }
    }
    sort(cands.begin(), cands.end(), [](const Cand& a, const Cand& b) {
        if (a.score != b.score) return a.score > b.score;
        if (a.h != b.h) return a.h > b.h;
        if (a.n != b.n) return a.n > b.n;
        return a.k < b.k;
    });
    return cands;
}

static vector<int> buildAdversarialPermutationLabelsByNode(int n, int k, int seed) {
    vector<int> parent(n + 1, 0), depth(n + 1, 0);
    for (int v = 2; v <= n; v++) parent[v] = (v - 2) / k + 1;
    int height = 0;
    for (int v = 2; v <= n; v++) {
        depth[v] = depth[parent[v]] + 1;
        height = max(height, depth[v]);
    }

    vector<int> rootChild(n + 1, 1);
    for (int v = 2; v <= n; v++) {
        int u = v;
        while (parent[u] != 1) u = parent[u];
        rootChild[v] = u;
    }

    vector<vector<vector<int>>> buckets;
    buckets.assign(k, vector<vector<int>>(height + 1));
    for (int v = 2; v <= n; v++) {
        int rc = rootChild[v]; 
        int idx = rc - 2;
        if (0 <= idx && idx < k) buckets[idx][depth[v]].push_back(v);
    }
    for (int i = 0; i < k; i++) {
        for (int d = 1; d <= height; d++) sort(buckets[i][d].begin(), buckets[i][d].end());
    }

    vector<int> order;
    order.reserve(n - 1);

    for (int d = height; d >= 1; d--) {
        bool takeBack = ((d + seed) & 1) == 0;
        while (true) {
            bool any = false;
            for (int idx = 0; idx < k; idx++) {
                auto &vec = buckets[idx][d];
                if (!vec.empty()) {
                    any = true;
                    int v;
                    if (takeBack) {
                        v = vec.back();
                        vec.pop_back();
                    } else {
                        v = vec.front();
                        vec.erase(vec.begin());
                    }
                    order.push_back(v);
                    if ((int)order.size() == n - 1) break;
                }
            }
            if (!any || (int)order.size() == n - 1) break;
        }
        if ((int)order.size() == n - 1) break;
    }

    if ((int)order.size() != n - 1) {
        vector<char> used(n + 1, 0);
        for (int v : order) used[v] = 1;
        for (int v = 2; v <= n; v++) if (!used[v]) order.push_back(v);
    }

    int rootLabel = n;
    vector<int> labelForNode(n + 1, 0);
    labelForNode[1] = rootLabel;

    vector<int> avail;
    avail.reserve(n - 1);
    for (int x = 1; x <= n; x++) if (x != rootLabel) avail.push_back(x);

    for (int i = 0; i < n - 1; i++) labelForNode[order[i]] = avail[i];

    vector<int> perm(n + 1, 0);
    for (int i = 1; i <= n; i++) perm[i] = labelForNode[i];
    return perm;
}

int main(int argc, char* argv[]) {
    registerGen(argc, argv, 1);

    string mode = opt<string>("mode", "non");
    int maxn = opt<int>("maxn", 1500);
    int nOpt = opt<int>("n", 0);
    int kOpt = opt<int>("k", 0);

    int seed = 1;
    if (argc >= 2) {
        seed = atoi(argv[1]);
        if (seed <= 0) seed = 1;
    }

    int n = 0, k = 0;

    if (nOpt > 0 && kOpt > 0) {
        n = nOpt;
        k = kOpt;
        ensuref(3 <= n && n <= maxn, "n out of range");
        ensuref(2 <= k && k < n, "k out of range");
        int h = computeHeightIfPerfect(n, k);
        ensuref(h >= 1, "n is not a perfect k-ary tree size for given k");
    } else {
        if (seed == 1) {
            n = 3; k = 2;
        } else if (seed == 2) {
            n = min(maxn, 1500);
            k = n - 1;
            ensuref(k >= 2, "maxn too small");
        } else if (seed == 3) {
            n = 1023; k = 2;
            if (n > maxn) {
                auto cands = allCandidatesUpTo(maxn);
                ensuref(!cands.empty(), "no valid candidates for maxn");
                for (auto &c : cands) {
                    if (c.k == 2) { n = c.n; k = c.k; break; }
                }
                if (n == 0) { n = cands[0].n; k = cands[0].k; }
            }
        } else {
            auto cands = allCandidatesUpTo(maxn);
            ensuref(!cands.empty(), "no valid candidates for maxn");
            int pickType = rnd.next(0, 99);
            if (pickType < 75) {
                int top = min<int>(60, (int)cands.size());
                int idx = rnd.next(0, top - 1);
                n = cands[idx].n;
                k = cands[idx].k;
            } else {
                int idx = rnd.next(0, (int)cands.size() - 1);
                n = cands[idx].n;
                k = cands[idx].k;
            }
        }
    }

    ensuref(3 <= n && n <= maxn, "internal n out of range");
    ensuref(2 <= k && k < n, "internal k out of range");
    ensuref(computeHeightIfPerfect(n, k) >= 1, "internal n,k not perfect");

    if (mode == "adp") {
        cout << n << " " << k << "\n";
        return 0;
    }

    ensuref(mode == "non", "unknown mode (use --mode=non or --mode=adp)");

    vector<int> perm;
    if (seed == 1 && n == 3 && k == 2) {
        
        
        perm.assign(n + 1, 0);
        perm[1] = 2;
        perm[2] = 1;
        perm[3] = 3;
    } else if (seed == 2 && k == n - 1) {
        perm.assign(n + 1, 0);
        perm[1] = n;
        for (int v = 2; v <= n; v++) perm[v] = v - 1;
    } else {
        perm = buildAdversarialPermutationLabelsByNode(n, k, seed);
    }

    vector<int> seen(n + 1, 0);
    for (int i = 1; i <= n; i++) {
        ensuref(1 <= perm[i] && perm[i] <= n, "perm out of range");
        ensuref(!seen[perm[i]], "perm not a permutation");
        seen[perm[i]] = 1;
    }

    cout << n << " " << k << "\n";
    for (int i = 1; i <= n; i++) {
        if (i > 1) cout << " ";
        cout << perm[i];
    }
    cout << "\n";
    return 0;
}