Problem:
問題 L :
L
番目の数字
反乱を嗅ぎつけた多くの G○○gle のコーダーが我々を取り押さえにやってきた．
かなりの数だ．
その中には，東京大学時代に僕らを優しく指導してくれた先輩たちも多く見受けられる．
残念ながら，今では敵同士だ．
wata
「予想以上の数だ…」
(iwi)
「ああ，我々が超高速指数時間アルゴリズムを持っているからと言って，それは彼らにようやく並んだに過ぎない．この数では…」
？？
「心配要らない！君たち，アルゴリズムをこっちに渡すんだ！」
声がした窓の外を見ると，G○○gle の建物に向きあう集団がいた．
黒の生地に赤で I，緑で ○，黄色で M と書かれた T シャツ … 彼らは I○M か！？
そして奇妙なことに，彼らは，向かってくる G○○gle のコーダー達を，
キーボードに触れることなく倒していた．
どうなっている？
(iwi)
「…！ ついに Wats○n 2 は完成したのか！」
wata
「どういうことだ？」
2011 年，I○M は彼らの高い技術力を活用し，
自然言語で出題されたクイズに高速かつ正確に応答するシステムである Wats○n を完成させ，
クイズ番組にてクイズ王と呼ばれてきた人間たちに勝利した．
そして 20XX 年の今，Wats○n は，
プログラミングコンテストの問題に対し解答のプログラムを作成するシステムとして生まれ変わったのだ．
I○M の登場により，我々を取り囲む人数が減ってゆく．
wata
「チャンスだ！半分は任せられるか？」
(iwi)
「もちろんだ！」
僕は，妄想と決別したことにより，全てを思い出していた．
当然，当時の知識や経験は，今から見ればレベルが低すぎて役に立たない．
しかし，一番大切なものを思い出すことができた．
それは，プログラミングコンテストを愛する気持ちである．
問題を開く瞬間のワクワク感，サンプルが通った瞬間の盛り上がり，
ステータスが更新される瞬間の緊張，勝利の瞬間の喜び．
(iwi)
「必ず kita_masa の復讐を果たし，この狂った世界を終わらせる！」
そして，僕は自信の 1 題を繰り出す．
これは，東京大学時代の自分が愛した問題「
K
番目の数字」を独自のテクニックにより一般化かつ大規模化した問題である．
問題
グラフが与えられる．
N
個の頂点があり，頂点に
1
から
N
の番号が付いている．
これらの頂点は，
N
- 1
本の辺によりツリー状に接続されている．
各頂点
v
には数字
a
v
が定まっている．
下図は，与えられるグラフの例である．
各頂点には上部に頂点の番号が，下部にその頂点に定められた数字 (
a
v
) が書かれている．
与えられるグラフの例
次に，
Q
個の以下のようなクエリが与えられる．
各クエリ
q
には，頂点
v
q
,
w
q
と整数
l
q
が指定される．
頂点
v
q
から頂点
w
q
への経路に含まれる頂点の数字のうち
l
q
番目に小さいものを求めよ．
経路は単純なもののみを考える．単純な経路とは，同じ頂点を二度通らない経路のことである．グラフがツリー状であることから，単純な経路は一意である．
経路には両端点（すなわち頂点
v
q
,
w
q
）を含むものとする．
例えば，上図のグラフが与えられている時，
(
v
q
,
w
q
,
l
q
) = (1, 6, 3)
なるクエリ
q
に対する答えは 7 である．
これは，頂点 1 から頂点 6 への経路には頂点 1, 3, 4, 6 が含まれており，
それらに定まった数字 2, 5, 8, 7 の 3 番目めに小さな数字は 7 であることによる．
グラフとクエリを受け取り，結果を出力するプログラムを作成せよ．
入力
入力の最初の行は 2 つの整数
N
,
Q
を含む．
これは，頂点数とクエリの数を表す．
続く
N
行は，各頂点の持つ数字を表す．
これらの行のうちの
v
行目は 1 つの整数
x
v
が書かれており，
これは頂点
v
の持つ数字を表す．
続く
N
- 1
行は，辺の情報を表す．
これらの行のうちの
e
行目は 2 つの整数
a
e
,
b
e
が書かれており，
これらは辺
e
が結ぶ 2 つの頂点を表す．
続く
Q
行は，クエリの情報を表す．
これらの行のうちの
q
行目は 3 つの整数
v
q
,
w
q
,
l
q
が書かれており，
これらはクエリ
q
の情報を表す．
出力
出力は
Q
行からなる．
q
行目にクエリ
q
の答えを出力せよ．
入力に関する制約
1 ≤
N
,
Q
≤ 10
5
1 ≤
x
v
≤ 10
9
1 ≤
a
e
,
b
e
≤
N
1 ≤
v
q
,
w
q
≤
N
頂点
v
q
から頂点
w
q
への経路には
l
q
個以上の頂点が存在する．
入出力例
入力例:
6 11
2
4
5
8
9
7
1 3
2 3
3 4
4 5
4 6
1 6 1
1 6 2
1 6 3
1 6 4
1 2 1
1 2 2
1 2 3
2 5 1
2 5 2
2 5 3
2 5 4
入力例に対する出力:
2
5
7
8
2
4
5
4
5
8
9

Submitted solutions:

code_snippet: // #pragma GCC target("avx2")
// #pragma GCC optimize("O3")
// #pragma GCC optimize("unroll-loops")

#include <bits/stdc++.h>
using namespace std;

// #include <atcoder/all>
// using namespace atcoder;

using ll = long long;
#define rep(i, n) for (ll i = 0; i < (ll)(n); ++i)

const ll dy[] = {-1, 0, 0, 1};
const ll dx[] = {0, -1, 1, 0};

template <class T, class T1, class T2> bool isrange(T target, T1 low, T2 high) { return low <= target && target < high; }
template <class T, class U> T min(const T &t, const U &u) { return t < u ? t : u; }
template <class T, class U> T max(const T &t, const U &u) { return t < u ? u : t; }
template <class T, class U> bool chmin(T &t, const U &u) { if (t > u) { t = u; return true; } return false; }
template <class T, class U> bool chmax(T &t, const U &u) { if (t < u) { t = u; return true; } return false; }

// #include "titan_cpplib/others/io.cpp"
// #include "titan_cpplib/others/print.cpp"
// #include "titan_cpplib/graph/hld.cpp"

#include <vector>
#include <stack>
using namespace std;

// HLD
namespace titan23 {

class HLD {
public:
    vector<vector<int>> G;
    int root, n;
    vector<int> size, par, dep, nodein, nodeout, head, hld;

private:
    void _dfs() {
        dep[root] = 0;
        stack<int> st;
        st.emplace(~root);
        st.emplace(root);
        while (!st.empty()) {
            int v = st.top(); st.pop();
            if (v >= 0) {
                int dep_nxt = dep[v] + 1;
                for (const int x: G[v]) {
                    if (dep[x] != -1) continue;
                    dep[x] = dep_nxt;
                    st.emplace(~x);
                    st.emplace(x);
                }
            } else {
                v = ~v;
                for (int i = 0; i < (int)G[v].size(); ++i) {
                    int x = G[v][i];
                    if (dep[x] < dep[v]) {
                        par[v] = x;
                        continue;
                    }
                    size[v] += size[x];
                    if (size[x] > size[G[v][0]]) {
                        swap(G[v][0], G[v][i]);
                    }
                }
            }
        }

        int curtime = 0;
        st.emplace(~root);
        st.emplace(root);
        while (!st.empty()) {
            int v = st.top(); st.pop();
            if (v >= 0) {
                if (par[v] == -1) {
                    head[v] = v;
                }
                nodein[v] = curtime;
                hld[curtime] = v;
                ++curtime;
                if (G[v].empty()) continue;
                int G_v0 = (int)G[v][0];
                for (int i = (int)G[v].size()-1; i >= 0; --i) {
                    int x = G[v][i];
                    if (x == par[v]) continue;
                    head[x] = (x == G_v0? head[v]: x);
                    st.emplace(~x);
                    st.emplace(x);
                }
            } else {
                nodeout[~v] = curtime;
            }
        }
    }

public:
    HLD() {}
    HLD(const vector<vector<int>> &G, const int root) :
            G(G), root(root), n(G.size()),
            size(n, 1), par(n, -1), dep(n, -1),
            nodein(n, -1), nodeout(n, -1), head(n, -1), hld(n, -1) {
        _dfs();
    }

    template<typename T>
    vector<T> build_list(vector<T> a) const {
        vector<T> res(a.size());
        for (int i = 0; i < n; ++i) {
            res[i] = a[hld[i]];
        }
        return res;
    }

    //! `u`, `v` の lca を返す / `O(logn)`
    int lca(int u, int v) const {
        while (true) {
            if (nodein[u] > nodein[v]) swap(u, v);
            if (head[u] == head[v]) return u;
            v = par[head[v]];
        }
    }

    vector<pair<int, int>> for_each_vertex_path(int u, int v) const {
        vector<pair<int, int>> res;
        while (head[u] != head[v]) {
            if (dep[head[u]] < dep[head[v]]) swap(u, v);
            res.emplace_back(nodein[head[u]], nodein[u]+1);
            u = par[head[u]];
        }
        if (dep[u] < dep[v]) swap(u, v);
        res.emplace_back(nodein[v], nodein[u]+1);
        return res;
    }

    pair<int, int> for_each_vertex_subtree(int v) const {
        return {nodein[v], nodeout[v]};
    }

    int dist(int u, int v) const {
        return dep[u] + dep[v] - 2 * dep[lca(u, v)];
    }
};
}  // namespace titan23
// #include "titan_cpplib/algorithm/zaatsu.cpp"
#include <vector>
using namespace std;

// Zaatsu
namespace titan23 {

/**
 * @brief 座標圧縮管理クラス
 */
template<typename T>
class Zaatsu {
private:
    vector<T> _to_origin;
    int n;

public:
    Zaatsu() : n(0) {}

    //! `used_items` からなる集合を管理するインスタンスを生成
    Zaatsu(vector<T> &used_items) : _to_origin(used_items) {
        sort(_to_origin.begin(), _to_origin.end());
        _to_origin.erase(unique(_to_origin.begin(), _to_origin.end()), _to_origin.end());
        n = (int)_to_origin.size();
    }

    //! 要素の種類数を返す(`max(to_zaatsu)`)
    int len() const {
        return n;
    }

    //! `x` を座標圧縮する
    int to_zaatsu(const T &x) const {
        return lower_bound(_to_origin.begin(), _to_origin.end(), x) - _to_origin.begin();
    }

    //! 座標圧縮された `x` を戻す
    T to_origin(const int &x) const {
        assert(0 <= x && x < n);
        return _to_origin[x];
    }
};
}  // namespace titan23
// #include "titan_cpplib/data_structures/wavelet_matrix.cpp"
#include <vector>
#include <queue>
// #include "titan_cpplib/data_structures/bit_vector.cpp"
#include <iostream>
#include <vector>
#include <cassert>
using namespace std;

// BitVector
namespace titan23 {

class BitVector {
private:
    using u64 = unsigned long long;
    int n, bsize;
    vector<u64> bit, acc;

public:
    BitVector() {}
    BitVector(const int n) :
        n(n), bsize((n+63)>>6), bit(bsize+1, 0), acc(bsize+1, 0) {}

    void set(const int k) {
        bit[k>>6] |= (1ull) << (k & 63);
    }

    void build() {
        for (int i = 0; i < bsize; ++i) {
            acc[i+1] += acc[i] + __builtin_popcountll(bit[i]);
        }
    }

    bool access(const int k) const {
        return (bit[k>>6] >> (k&63)) & 1;
    }

    int rank0(const int r) const {
        return r - (acc[r>>6] + __builtin_popcountll(bit[r>>6] & ((1ull << (r & 63)) - 1)));
    }

    int rank1(const int r) const {
        return acc[r>>6] + __builtin_popcountll(bit[r>>6] & ((1ull << (r & 63)) - 1));
    }

    int rank(const int r, const bool v) const {
        return v ? rank1(r) : rank0(r);
    }

    int select0(int k) const {
        if (k < 0 or rank0(n) <= k) return -1;
        int l = 0, r = bsize+1;
        while (r - l > 1) {
            const int m = (l + r) >> 1;
            if (m*32 - acc[m] > k) {
                r = m;
            } else {
                l = m;
            }
        }
        int indx = 32 * l;
        k = k - (l*32 - acc[l]) + rank0(indx);
        l = indx; r = indx+32;
        while (r - l > 1) {
            const int m = (l + r) >> 1;
            if (rank0(m) > k) {
                r = m;
            } else {
                l = m;
            }
        }
        return l;
    }

    int select1(int k) const {
        if (k < 0 || rank1(n) <= k) return -1;
        int l = 0, r = bsize+1;
        while (r - l > 1) {
            const int m = (l + r) >> 1;
            if (acc[m] > k) {
                r = m;
            } else {
                l = m;
            }
        }
        int indx = 32 * l;
        k = k - acc[l] + rank1(indx);
        l = indx; r = indx+32;
        while (r - l > 1) {
            const int m = (l + r) >> 1;
            if (rank1(m) > k) {
                r = m;
            } else {
                l = m;
            }
        }
        return l;
    }

    int select(const int k, const int v) const {
        return v ? select1(k) : select0(k);
    }

    int len() const {
        return n;
    }

    void print() const {
        cout << "[";
        for (int i = 0; i < len()-1; ++i) {
            cout << access(i) << ", ";
        }
        if (len() > 0) {
            cout << access(len()-1);
        }
        cout << "]\n";
    }
};
}  // namespace titan23
using namespace std;

// WaveletMatrix
namespace titan23 {

template<typename T>
class WaveletMatrix {
private:
    T sigma;
    int log;
    vector<BitVector> v;
    vector<int> mid;
    int n;

    int bit_length(const int n) const {
        return n == 0 ? 0 : 32 - __builtin_clz(n);
    }

    void build(vector<T> a) {
        for (int bit = log-1; bit >= 0; --bit) {
            vector<T> zero, one;
            v[bit] = BitVector(n);
            for (int i = 0; i < n; ++i) {
                if ((a[i] >> bit) & 1) {
                    v[bit].set(i);
                    one.emplace_back(a[i]);
                } else {
                    zero.emplace_back(a[i]);
                }
            }
            v[bit].build();
            mid[bit] = zero.size();
            a = zero;
            a.insert(a.end(), one.begin(), one.end());
            assert(a.size() == n);
        }
    }

public:
    WaveletMatrix() {}

    WaveletMatrix(const T sigma)
        : sigma(sigma), log(bit_length(sigma-1)), v(log), mid(log), n(0) {}

    WaveletMatrix(const T sigma, const vector<T> &a)
        : sigma(sigma), log(bit_length(sigma-1)), v(log), mid(log), n(a.size()) {
        build(a);
    }

    T access(int k) const {
        T s = 0;
        for (int bit = log-1; bit >= 0; --bit) {
            if (v[bit].access(k)) {
                s |= (T)1 << bit;
                k = v[bit].rank1(k) + mid[bit];
            } else {
                k = v[bit].rank0(k);
            }
        }
        return s;
    }

    //! `a[0, r)` に含まれる `x` の個数を返します。
    int rank(int r, T x) const {
        int l = 0;
        for (int bit = log-1; bit >= 0; --bit) {
            if ((x >> bit) & 1) {
                l = v[bit].rank1(l) + mid[bit];
                r = v[bit].rank1(r) + mid[bit];
            } else {
                l = v[bit].rank0(l);
                r = v[bit].rank0(r);
            }
        }
        return r - l;
    }

    // `k` 番目の `v` のインデックスを返す。
    int select(int k, T x) const {
        int s = 0;
        for (int bit = log-1; bit >= 0; --bit) {
            if ((x >> bit) & 1) {
                s = v[bit].rank0(n) + v[bit].rank1(s);
            } else {
                s = v[bit].rank0(s);
            }
        }
        s += k;
        for (int bit = 0; bit < log; ++bit) {
            if ((x >> bit) & 1) {
                s = v[bit].select1(s - v[bit].rank0(n));
            } else {
                s = v[bit].select0(s);
            }
        }
        return s;
    }

    // `a[l, r)` の中で k 番目に **小さい** 値を返します。
    T kth_smallest(int l, int r, int k) const {
        T s = 0;
        for (int bit = log-1; bit >= 0; --bit) {
            const int r0 = v[bit].rank0(r), l0 = v[bit].rank0(l);
            const int cnt = r0 - l0;
            if (cnt <= k) {
                s |= (T)1 << bit;
                k -= cnt;
                l = l - l0 + mid[bit];
                r = r - r0 + mid[bit];
            } else {
                l = l0;
                r = r0;
            }
        }
        return s;
    }

    T kth_largest(int l, int r, int k) const {
        return kth_smallest(l, r, r-l-k-1);
    }

    // `a[l, r)` の中で、要素を出現回数が多い順にその頻度とともに `k` 個返します。
    vector<pair<int, int>> topk(int l, int r, int k) {
        // heap[-length, x, l, bit]
        priority_queue<tuple<int, T, int, int>> hq;
        hq.emplace(r-l, 0, l, log-1);
        vector<pair<T, int>> ans;
        while (!hq.empty()) {
            auto [length, x, l, bit] = hq.top();
            hq.pop();
            if (bit == -1) {
                ans.emplace_back(x, length);
                k -= 1;
                if (k == 0) break;
            } else {
                r = l + length;
                int l0 = v[bit].rank0(l);
                int r0 = v[bit].rank0(r);
                if (l0 < r0) hq.emplace(r0-l0, x, l0, bit-1);
                int l1 = v[bit].rank1(l) + mid[bit];
                int r1 = v[bit].rank1(r) + mid[bit];
                if (l1 < r1) hq.emplace(r1-l1, x|((T)1<<bit), l1, bit-1);
            }
        }
        return ans;
    }

    T sum(int l, int r) const {
        assert(false);
        T s = 0;
        for (const auto &[sum, cnt]: topk(l, r, r-l)) {
            s += sum * cnt;
        }
        return s;
    }

    // a[l, r) で x 未満の要素の数を返す'''
    int range_freq(int l, int r, T x) const {
        int ans = 0;
        for (int bit = log-1; bit >= 0; --bit) {
            int l0 = v[bit].rank0(l), r0 = v[bit].rank0(r);
            if ((x >> bit) & 1) {
                ans += r0 - l0;
                l += mid[bit] - l0;
                r += mid[bit] - r0;
            } else {
                l = l0;
                r = r0;
            }
        }
        return ans;
    }

    //`a[l, r)` に含まれる、 `x` 以上 `y` 未満である要素の個数を返します。
    int range_freq(int l, int r, T x, T y) const {
        return range_freq(l, r, y) - range_freq(l, r, x);
    }

    //`a[l, r)` で、`y`未満であるような要素のうち最大の要素を返します。
    T prev_value(int l, int r, T y) const {
        int x = range_freq(l, r, y);
        if (x == 0) {
            return -1;
        }
        return kth_smallest(l, r, x-1);
    }

    T next_value(int l, int r, T x) const {
        return kth_smallest(l, r, range_freq(l, r, x));
    }

    //`a[l, r)` に含まれる `x` の個数を返します。
    int range_count(int l, int r, T x) const {
        return rank(r, x) - rank(l, x);
    }

    int len() const {
        return n;
    }

    friend ostream& operator<<(ostream& os, const titan23::WaveletMatrix<T>& wm) {
        int n = wm.len();
        os << "[";
        for (int i = 0; i < n; ++i) {
            os << wm.access(i);
            if (i != n-1) os << ", ";
        }
        os << "]";
        return os;
    }
};
}  // namespace titan23


void solve() {
    int n, q; cin >> n >> q;
    vector<int> X(n);
    rep(i, n) cin >> X[i];
    vector<vector<int>> G(n);
    rep(i, n-1) {
        int a, b; cin >> a >> b;
        --a; --b;
        G[a].emplace_back(b);
        G[b].emplace_back(a);
    }

    int root = 0;
    titan23::HLD hld(G, root);
    titan23::Zaatsu<int> Z(X);
    vector<int> W(n);
    rep(i, n) W[i] = Z.to_zaatsu(X[i]);
    titan23::WaveletMatrix<int> wm(n, hld.build_list(W));

    rep(qdx, q) {
        int v, w, k; cin >> v >> w >> k;
        --v; --w;

        auto isok = [&] (int M) -> bool {
            vector<pair<int, int>> path = hld.for_each_vertex_path(v, w);
            int cnt = 0;
            for (auto &[u, v] : path) {
                cnt += wm.range_freq(u, v, M+1);
            }
            return cnt >= k;
        };

        int ok = Z.len()-1, ng = -1;
        while (ok - ng > 1) {
            int mid = (ok + ng) / 2;
            if (isok(mid)) {
                ok = mid;
            } else {
                ng = mid;
            }
        }
        cout << Z.to_origin(ok) << "\n";
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << fixed << setprecision(15);
    cerr << fixed << setprecision(15);

    int t = 1;
    // cin >> t;
    for (int i = 0; i < t; ++i) {
        solve();
    }

    return 0;
}
accuracy: 1
time_ms: 2030
memory_kb: 20580
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;

class HLDecomposition {
	vector<vector<int>> g;
	vector<int> vid, head, heavy, parent, depth;
public:
	vector<int> inv;
private:
	int dfs(int curr, int prev) {
		parent[curr] = prev;
		int sub = 1, max_sub = 0;
		for (int next : g[curr]) if (next != prev) {
			depth[next] = depth[curr] + 1;
			int sub_next = dfs(next, curr);
			sub += sub_next;
			if (max_sub < sub_next) max_sub = sub_next, heavy[curr] = next;
		}
		return sub;
	}
	void bfs() {
		int k = 0;
		queue<int> q({ 0 });
		while (!q.empty()) {
			int h = q.front(); q.pop();
			for (int i = h; i != -1; i = heavy[i]) {
				vid[i] = k++;
				inv[vid[i]] = i;
				head[i] = h;
				for (int j : g[i]) if (j != parent[i] && j != heavy[i]) q.push(j);
			}
		}
	}
public:
	HLDecomposition(int n)
		: g(n), vid(n, -1), head(n), heavy(n, -1), parent(n), depth(n), inv(n) {}

	void add(int u, int v) {
		g[u].push_back(v);
		g[v].push_back(u);
	}
	void build() {
		dfs(0, -1);
		bfs();
	}
	void for_each(int u, int v, function<void(int, int)> f) {
		if (vid[u] > vid[v]) swap(u, v);
		f(max(vid[head[v]], vid[u]), vid[v]);
		if (head[u] != head[v]) for_each(u, parent[head[v]], f);
	}
};

template <typename T>
class SegmentTree {
	const int n;
	vector<vector<T>> data;
	vector<T> merge(const vector<T>& l, const vector<T>& r) {
		int nl = l.size(), nr = r.size();
		vector<T> res(nl + nr);
		for (int i = 0, j = 0, k = 0; i < nl || j < nr; k++) {
			if (j == nr)
				res[k] = l[i++];
			else if (i == nl)
				res[k] = r[j++];
			else if (l[i] <= r[j])
				res[k] = l[i++];
			else
				res[k] = r[j++];
		}
		return res;
	}
	int size(int n) {
		int res;
		for (res = 1; res < n; res <<= 1);
		return res;
	}
	T sub(int l, int r, int node, int lb, int ub, T val) {
		if (ub <= l || r <= lb) return 0;
		if (l <= lb && ub <= r) return upper_bound(data[node].begin(), data[node].end(), val) - data[node].begin();
		return sub(l, r, node * 2, lb, (lb + ub) / 2, val) + sub(l, r, node * 2 + 1, (lb + ub) / 2, ub, val);
	}
public:
	SegmentTree(const vector<T>& data_) : n(size(data_.size())), data(n * 2) {
		for (int i = 0; i < (int)data_.size(); i++)
			data[i + n].push_back(data_[i]);
		for (int i = n - 1; i >= 0; i--)
			data[i] = merge(data[i * 2], data[i * 2 + 1]);
	}
	void Update(int p, T val) {
		p += n;
		data[p][0] = val;
		while (p >>= 1) data[p] = merge(data[p * 2], data[p * 2 + 1]);
	}
	void Add(int p, T val) {
		p += n;
		data[p][0] += val;
		while (p >>= 1) data[p] = merge(data[p * 2], data[p * 2 + 1]);
	}
	T Find(int l, int r, T val) {
		return sub(l, r + 1, 1, 0, n, val);
	}
};

int main()
{
	cin.sync_with_stdio(false);
	cin.tie(0);
	int N, Q;
	cin >> N >> Q;
	vector<int> W(N), base(N);
	for (int i = 0; i < N; i++) {
		cin >> W[i];
	}
	HLDecomposition hl(N);
	for (int i = 0, u, v; i < N - 1; i++) {
		cin >> u >> v; u--; v--;
		hl.add(u, v);
	}
	hl.build();
	for (int i = 0; i < N; i++) {
		base[i] = W[hl.inv[i]];
	}
	int v, w, l;
	SegmentTree<int> st(base);
	while (Q--) {
		cin >> v >> w >> l; v--; w--;
		int lb = 0, ub = 1000000000;
		while (lb + 1 < ub) {
			int c = lb + (ub - lb) / 2;
			int sum = 0;
			hl.for_each(v, w, [&](int ll, int rr) {
				sum += st.Find(ll, rr, c);
			});
			if (sum >= l) {
				ub = c;
			}
			else {
				lb = c;
			}
		}
		printf("%d\n", ub);
	}
	return 0;
}
accuracy: 1
time_ms: 2310
memory_kb: 34536
final_rank: 9

code_snippet: // competitive-verifier: PROBLEM
#include <cmath>
#include <cstdint>
#include <numeric>
/// @brief めぐる式二分探索
template <class T, class F>
std::int64_t meguru_binary_search(T ok, T ng, F check) {
    while (std::abs(ok - ng) > 1) {
        T mid = std::midpoint(ok, ng);
        (check(mid) ? ok : ng) = mid;
    }
    return ok;
}
#include <algorithm>
#include <iterator>
#include <vector>
/// @brief 座標圧縮
template <class T>
struct coordinate_compression {
    coordinate_compression() = default;
    coordinate_compression(const std::vector<T> &_data) : data(_data) { build(); }
    const T &operator[](int i) const { return data[i]; }
    void add(T x) { data.emplace_back(x); }
    void build() {
        std::sort(data.begin(), data.end());
        data.erase(std::unique(data.begin(), data.end()), data.end());
    }
    bool exists(T x) const {
        auto it = std::lower_bound(data.begin(), data.end(), x);
        return it != data.end() && *it == x;
    }
    int get(T x) const {
        return std::distance(data.begin(), std::lower_bound(data.begin(), data.end(), x));
    }
    int lower_bound(T x) const {
        return std::distance(data.begin(), std::lower_bound(data.begin(), data.end(), x));
    }
    int upper_bound(T x) const {
        return std::distance(data.begin(), std::upper_bound(data.begin(), data.end(), x));
    }
    std::vector<int> compress(const std::vector<T> &v) const {
        int n = v.size();
        std::vector<int> res(n);
        for (int i = 0; i < n; ++i) res[i] = get(v[i]);
        return res;
    }
    int size() const { return data.size(); }
  private:
    std::vector<T> data;
};
/// @brief 座標圧縮
template <class T>
std::vector<int> compress(const std::vector<T> &v) {
    coordinate_compression cps(v);
    std::vector<int> res;
    res.reserve(std::size(v));
    for (auto &&x : v) res.emplace_back(cps.get(x));
    return res;
}
#include <cassert>
#include <tuple>
namespace internal {
/**
 * @brief 完備辞書
 *
 * @see https://ei1333.github.io/library/structure/wavelet/succinct-indexable-dictionary.hpp
 */
struct bit_vector {
    bit_vector() = default;
    bit_vector(unsigned int _length)
        : length(_length), blocks((_length + 31) >> 5), bit((_length + 31) >> 5),
          sum((_length + 31) >> 5) {}
    void set(unsigned int k) { bit[k >> 5] |= 1U << (k & 31); }
    void build() {
        sum[0] = 0U;
        for (unsigned int i = 1; i < blocks; ++i) {
            sum[i] = sum[i - 1] + __builtin_popcount(bit[i - 1]);
        }
    }
    bool operator[](unsigned int k) const { return bit[k >> 5] >> (k & 31) & 1; }
    unsigned int rank(unsigned int k) const {
        return sum[k >> 5] + __builtin_popcount(bit[k >> 5] & ((1U << (k & 31)) - 1));
    }
    unsigned int rank(bool val, unsigned int k) const { return val ? rank(k) : k - rank(k); }
    unsigned int select(unsigned int k) const {
        unsigned int sl = 0, sr = blocks + 1;
        while (sr - sl > 1) {
            unsigned int m = (sl + sr) >> 1;
            if (sum[m] < k) sl = m;
            else sr = m;
        }
        k -= sum[sl];
        unsigned int bl = 0, br = 32;
        while (br - bl > 1) {
            unsigned int m = (bl + br) >> 1;
            if (__builtin_popcount(bit[sl] & ((1U << m) - 1)) < k) bl = m;
            else br = m;
        }
        return (sl << 5) + bl;
    }
  private:
    unsigned int length, blocks;
    std::vector<unsigned int> bit, sum;
};
}  // namespace internal
/// @brief 重み付きウェーブレット行列
template <class T, class U = T, int L = 30>
struct wavelet_matrix_rectangle_sum {
    wavelet_matrix_rectangle_sum() = default;
    template <class Value>
    wavelet_matrix_rectangle_sum(const std::vector<T> &v, const std::vector<Value> &u)
        : length(v.size()) {
        assert(v.size() == u.size());
        std::vector<int> l(length), r(length), ord(length);
        std::iota(ord.begin(), ord.end(), 0);
        for (int level = L - 1; level >= 0; level--) {
            matrix[level] = internal::bit_vector(length + 1);
            int left = 0, right = 0;
            for (int i = 0; i < length; i++) {
                if ((v[ord[i]] >> level) & 1) {
                    matrix[level].set(i);
                    r[right++] = ord[i];
                } else {
                    l[left++] = ord[i];
                }
            }
            mid[level] = left;
            matrix[level].build();
            ord.swap(l);
            for (int i = 0; i < right; i++) ord[left + i] = r[i];
            cs[level].resize(length + 1);
            cs[level][0] = U(0);
            for (int i = 0; i < length; i++) cs[level][i + 1] = cs[level][i] + u[ord[i]];
        }
    }
    U kth_smallest_sum(int l, int r, int k) const {
        assert(0 <= k && k <= r - l);
        T val = T();
        U res = U();
        for (int level = L - 1; level >= 0; --level) {
            int cnt = matrix[level].rank(false, r) - matrix[level].rank(false, l);
            bool f = cnt <= k;
            if (f) {
                val |= T(1) << level;
                res += cs[level][matrix[level].rank(false, r)] -
                       cs[level][matrix[level].rank(false, l)];
                k -= cnt;
            }
            std::tie(l, r) = succ(f, l, r, level);
        }
        return res + val * k;
    }
    U kth_largest_sum(int l, int r, int k) const {
        return cs[L - 1][matrix[L - 1].rank(false, r)] + cs[L - 1][matrix[L - 1].rank(true, r)] -
               cs[L - 1][matrix[L - 1].rank(false, l)] - cs[L - 1][matrix[L - 1].rank(true, l)] -
               kth_smallest_sum(l, r, r - l - k);
    }
    U range_sum(int r, T x) const { return range_sum(0, r, x); }
    U range_sum(int l, int r, T x) const {
        for (int level = L - 1; level >= 0; level--)
            std::tie(l, r) = succ((x >> level) & 1, l, r, level);
        return cs[0][matrix[0].rank(false, r)] - cs[0][matrix[0].rank(false, l)];
    }
    U rect_sum(int l, int r, T upper) const {
        U res = 0;
        for (int level = L - 1; level >= 0; level--) {
            bool f = (upper >> level) & 1;
            if (f)
                res += cs[level][matrix[level].rank(false, r)] -
                       cs[level][matrix[level].rank(false, l)];
            std::tie(l, r) = succ(f, l, r, level);
        }
        return res;
    }
    U rect_sum(int l, int r, T lower, T upper) const {
        return rect_sum(l, r, upper) - rect_sum(l, r, lower);
    }
  private:
    int length;
    internal::bit_vector matrix[L];
    int mid[L];
    std::vector<U> cs[L];
    std::pair<int, int> succ(bool f, int l, int r, int level) const {
        return {matrix[level].rank(f, l) + mid[level] * f,
                matrix[level].rank(f, r) + mid[level] * f};
    }
};
#include <iostream>
/**
 * @brief 重み付きグラフ
 *
 * @tparam T 辺の重みの型
 */
template <class T>
struct Graph {
  private:
    struct _edge {
        constexpr _edge() : _from(), _to(), _weight() {}
        constexpr _edge(int from, int to, T weight) : _from(from), _to(to), _weight(weight) {}
        constexpr bool operator<(const _edge &rhs) const { return weight() < rhs.weight(); }
        constexpr bool operator>(const _edge &rhs) const { return rhs < *this; }
        constexpr int from() const { return _from; }
        constexpr int to() const { return _to; }
        constexpr T weight() const { return _weight; }
      private:
        int _from, _to;
        T _weight;
    };
  public:
    using edge_type = typename Graph<T>::_edge;
    Graph() : _size(), edges() {}
    Graph(int v) : _size(v), edges(v) {}
    const auto &operator[](int i) const { return edges[i]; }
    auto &operator[](int i) { return edges[i]; }
    const auto begin() const { return edges.begin(); }
    auto begin() { return edges.begin(); }
    const auto end() const { return edges.end(); }
    auto end() { return edges.end(); }
    constexpr int size() const { return _size; }
    void add_edge(const edge_type &e) { edges[e.from()].emplace_back(e); }
    void add_edge(int from, int to, T weight = T(1)) { edges[from].emplace_back(from, to, weight); }
    void add_edges(int from, int to, T weight = T(1)) {
        edges[from].emplace_back(from, to, weight);
        edges[to].emplace_back(to, from, weight);
    }
    void input_edge(int m, int base = 1) {
        for (int i = 0; i < m; ++i) {
            int from, to;
            T weight;
            std::cin >> from >> to >> weight;
            add_edge(from - base, to - base, weight);
        }
    }
    void input_edges(int m, int base = 1) {
        for (int i = 0; i < m; ++i) {
            int from, to;
            T weight;
            std::cin >> from >> to >> weight;
            add_edges(from - base, to - base, weight);
        }
    }
  private:
    int _size;
    std::vector<std::vector<edge_type>> edges;
};
template <>
struct Graph<void> {
  private:
    struct _edge {
        constexpr _edge() : _from(), _to() {}
        constexpr _edge(int from, int to) : _from(from), _to(to) {}
        constexpr int from() const { return _from; }
        constexpr int to() const { return _to; }
        constexpr int weight() const { return 1; }
        constexpr bool operator<(const _edge &rhs) const { return weight() < rhs.weight(); }
        constexpr bool operator>(const _edge &rhs) const { return rhs < *this; }
      private:
        int _from, _to;
    };
  public:
    using edge_type = typename Graph<void>::_edge;
    Graph() : _size(), edges() {}
    Graph(int v) : _size(v), edges(v) {}
    const auto &operator[](int i) const { return edges[i]; }
    auto &operator[](int i) { return edges[i]; }
    const auto begin() const { return edges.begin(); }
    auto begin() { return edges.begin(); }
    const auto end() const { return edges.end(); }
    auto end() { return edges.end(); }
    constexpr int size() const { return _size; }
    void add_edge(const edge_type &e) { edges[e.from()].emplace_back(e); }
    void add_edge(int from, int to) { edges[from].emplace_back(from, to); }
    void add_edges(int from, int to) {
        edges[from].emplace_back(from, to);
        edges[to].emplace_back(to, from);
    }
    void input_edge(int m, int base = 1) {
        for (int i = 0; i < m; ++i) {
            int from, to;
            std::cin >> from >> to;
            add_edge(from - base, to - base);
        }
    }
    void input_edges(int m, int base = 1) {
        for (int i = 0; i < m; ++i) {
            int from, to;
            std::cin >> from >> to;
            add_edges(from - base, to - base);
        }
    }
  private:
    int _size;
    std::vector<std::vector<edge_type>> edges;
};
#ifdef ATCODER
#pragma GCC target("sse4.2,avx512f,avx512dq,avx512ifma,avx512cd,avx512bw,avx512vl,bmi2")
#endif
#pragma GCC optimize("Ofast,fast-math,unroll-all-loops")
#include <bits/stdc++.h>
#ifndef ATCODER
#pragma GCC target("sse4.2,avx2,bmi2")
#endif
template <class T, class U>
constexpr bool chmax(T &a, const U &b) {
    return a < (T)b ? a = (T)b, true : false;
}
template <class T, class U>
constexpr bool chmin(T &a, const U &b) {
    return (T)b < a ? a = (T)b, true : false;
}
constexpr std::int64_t INF = 1000000000000000003;
constexpr int Inf = 1000000003;
constexpr double EPS = 1e-7;
constexpr double PI = 3.14159265358979323846;
#define FOR(i, m, n) for (int i = (m); i < int(n); ++i)
#define FORR(i, m, n) for (int i = (m)-1; i >= int(n); --i)
#define FORL(i, m, n) for (int64_t i = (m); i < int64_t(n); ++i)
#define rep(i, n) FOR (i, 0, n)
#define repn(i, n) FOR (i, 1, n + 1)
#define repr(i, n) FORR (i, n, 0)
#define repnr(i, n) FORR (i, n + 1, 1)
#define all(s) (s).begin(), (s).end()
struct Sonic {
    Sonic() {
        std::ios::sync_with_stdio(false);
        std::cin.tie(nullptr);
        std::cout << std::fixed << std::setprecision(20);
    }
    constexpr void operator()() const {}
} sonic;
using namespace std;
using ll = std::int64_t;
using ld = long double;
template <class T, class U>
std::istream &operator>>(std::istream &is, std::pair<T, U> &p) {
    return is >> p.first >> p.second;
}
template <class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
    for (T &i : v) is >> i;
    return is;
}
template <class T, class U>
std::ostream &operator<<(std::ostream &os, const std::pair<T, U> &p) {
    return os << '(' << p.first << ',' << p.second << ')';
}
template <class T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
    for (auto it = v.begin(); it != v.end(); ++it) os << (it == v.begin() ? "" : " ") << *it;
    return os;
}
template <class Head, class... Tail>
void co(Head &&head, Tail &&...tail) {
    if constexpr (sizeof...(tail) == 0) std::cout << head << '\n';
    else std::cout << head << ' ', co(std::forward<Tail>(tail)...);
}
template <class Head, class... Tail>
void ce(Head &&head, Tail &&...tail) {
    if constexpr (sizeof...(tail) == 0) std::cerr << head << '\n';
    else std::cerr << head << ' ', ce(std::forward<Tail>(tail)...);
}
void Yes(bool is_correct = true) { std::cout << (is_correct ? "Yes\n" : "No\n"); }
void No(bool is_not_correct = true) { Yes(!is_not_correct); }
void YES(bool is_correct = true) { std::cout << (is_correct ? "YES\n" : "NO\n"); }
void NO(bool is_not_correct = true) { YES(!is_not_correct); }
void Takahashi(bool is_correct = true) { std::cout << (is_correct ? "Takahashi" : "Aoki") << '\n'; }
void Aoki(bool is_not_correct = true) { Takahashi(!is_not_correct); }
#include <bit>
/// @brief スパーステーブル
template <class M>
struct sparse_table {
  private:
    using T = typename M::value_type;
  public:
    sparse_table() = default;
    sparse_table(const std::vector<T> &v) : _size(v.size()), data() {
        int b = std::max(1, std::countr_zero(std::bit_ceil<unsigned>(_size)));
        data.emplace_back(v);
        for (int i = 1; i < b; ++i) data.emplace_back(_size + 1 - (1 << i));
        for (int i = 1; i < b; ++i) {
            for (int j = 0; j + (1 << i) <= _size; ++j) {
                data[i][j] = M::op(data[i - 1][j], data[i - 1][j + (1 << (i - 1))]);
            }
        }
    }
    T prod(int l, int r) const {
        assert(0 <= l && l <= r && r <= _size);
        if (l == r) return M::id();
        if (l + 1 == r) return data[0][l];
        int b = 31 - std::countl_zero<unsigned>(r - l - 1);
        return M::op(data[b][l], data[b][r - (1 << b)]);
    }
  private:
    int _size;
    std::vector<std::vector<T>> data;
};
namespace internal {
template <class T, int N>
struct fixed_stack {
    constexpr fixed_stack() : _size(), _data() {}
    constexpr T top() const { return _data[_size - 1]; }
    constexpr bool empty() const { return _size == 0; }
    constexpr int size() const { return _size; }
    constexpr void emplace(const T &e) { _data[_size++] = e; }
    constexpr void emplace(T &&e) { _data[_size++] = e; }
    constexpr void pop() { --_size; }
    constexpr void clear() { _size = 0; }
  private:
    int _size;
    std::array<T, N> _data;
};
}  // namespace internal
/**
 * @brief 線形 Sparse Table
 *
 * @tparam M
 */
template <class M>
struct linear_sparse_table {
  private:
    using T = M::value_type;
    static constexpr int W = 64;
  public:
    linear_sparse_table() = default;
    linear_sparse_table(const std::vector<T> &v) : _size(v.size()), data(v) {
        int n = v.size();
        int b = n / W;
        internal::fixed_stack<int, W + 1> st;
        std::vector<T> u(b);
        word_data.resize(b + (n > b * W));
        for (int i = 0; i < b; ++i) {
            T m = M::id();
            std::uint64_t bit = 0;
            std::vector<std::uint64_t> bits(W);
            for (int j = 0; j < W; ++j) {
                m = M::op(m, v[i * W + j]);
                while (!st.empty() && M::op(v[i * W + st.top()], v[i * W + j]) == v[i * W + j]) {
                    bit ^= std::uint64_t(1) << st.top();
                    st.pop();
                }
                bits[j] = bit;
                bit |= std::uint64_t(1) << j;
                st.emplace(j);
            }
            u[i] = m;
            word_data[i] = bits;
            st.clear();
        }
        if (n > b * W) {
            std::uint64_t bit = 0;
            std::vector<std::uint64_t> bits(n - b * W);
            for (int j = 0; j < n - b * W; ++j) {
                while (!st.empty() && M::op(v[b * W + st.top()], v[b * W + j]) == v[b * W + j]) {
                    bit ^= std::uint64_t(1) << st.top();
                    st.pop();
                }
                bits[j] = bit;
                bit |= std::uint64_t(1) << j;
                st.emplace(j);
            }
            word_data[b] = bits;
        }
        block_table = sparse_table<M>(u);
    }
    const T &operator[](int k) const { return data[k]; }
    T prod(int l, int r) const {
        assert(0 <= l && l < r && r <= _size);
        int lb = (l + W - 1) / W, rb = r / W;
        if (lb > rb) return word_prod(l, r);
        T res = (lb == rb ? M::id() : block_table.prod(lb, rb));
        if (l < lb * W) res = M::op(res, word_prod(l, lb * W));
        if (rb * W < r) res = M::op(res, word_prod(rb * W, r));
        return res;
    }
  private:
    int _size;
    std::vector<T> data;
    sparse_table<M> block_table;
    std::vector<std::vector<std::uint64_t>> word_data;
    T word_prod(int l, int r) const {
        if (l == r) return M::id();
        int b = l / W;
        int lw = l - b * W, rw = r - b * W;
        if ((word_data[b][rw - 1] >> lw) == 0ul) return data[r - 1];
        return data[l + std::countr_zero(word_data[b][rw - 1] >> lw)];
    }
};
struct linear_lca {
  private:
    struct S {
        int depth, index;
        bool operator<(const S &rhs) const { return depth < rhs.depth; }
        bool operator==(const S &rhs) const = default;
    };
    struct M {
        using value_type = S;
        static constexpr S id() { return S{std::numeric_limits<int>::max(), -1}; }
        static constexpr S op(const S &lhs, const S &rhs) { return std::min(lhs, rhs); }
    };
  public:
    template <class T>
    linear_lca(const Graph<T> &g, int r = 0) : ord(g.size(), -1), lst() {
        std::vector<S> v;
        std::stack<std::pair<int, int>> st;
        st.emplace(r, 0);
        while (!st.empty()) {
            auto [x, d] = st.top();
            st.pop();
            if (x < 0) {
                v.emplace_back(d, ~x);
                continue;
            }
            ord[x] = v.size();
            v.emplace_back(d, x);
            for (auto e : g[x]) {
                if (ord[e.to()] != -1) continue;
                st.emplace(~x, d);
                st.emplace(e.to(), d + 1);
            }
        }
        lst = linear_sparse_table<M>(v);
    }
    int operator()(int u, int v) const { return lca(u, v); }
    int lca(int u, int v) const {
        auto [l, r] = std::minmax(ord[u], ord[v]);
        return lst.prod(l, r + 1).index;
    }
  private:
    std::vector<int> ord;
    linear_sparse_table<M> lst;
};
int main(void) {
    int n, q;
    cin >> n >> q;
    vector<int> a(n);
    cin >> a;
    coordinate_compression cps(a);
    a = cps.compress(a);
    Graph<void> g(n);
    g.input_edges(n - 1);
    vector<int> in(n), out(n), ord(n * 2), w(n * 2);
    int c = 0;
    auto dfs = [&](auto self, int x, int p) -> void {
        in[x] = c;
        w[c] = 1;
        ord[c++] = a[x];
        for (auto e : g[x]) {
            if (e.to() == p)
                continue;
            self(self, e.to(), x);
        }
        w[c] = -1;
        ord[c++] = a[x];
        out[x] = c;
    };
    dfs(dfs, 0, -1);
    linear_lca lca(g);
    wavelet_matrix_rectangle_sum<int, int, 18> wm(ord, w);
    while (q--) {
        int u, v, k;
        cin >> u >> v >> k;
        --u, --v;
        auto f = [&](int y) {
            return wm.rect_sum(0, in[u] + 1, y) + wm.rect_sum(0, in[v] + 1, y) -
                       wm.rect_sum(0, in[lca(u, v)] + 1, y) - wm.rect_sum(0, in[lca(u, v)], y) <
                   k;
        };
        auto ans = meguru_binary_search(0, cps.size() + 1, f);
        co(cps[ans]);
    }
    return 0;
}
accuracy: 1
time_ms: 720
memory_kb: 39676
final_rank: 4

code_snippet: // competitive-verifier: PROBLEM
#include <cmath>
#include <cstdint>
#include <numeric>
/// @brief めぐる式二分探索
template <class T, class F>
std::int64_t meguru_binary_search(T ok, T ng, F check) {
    while (std::abs(ok - ng) > 1) {
        T mid = std::midpoint(ok, ng);
        (check(mid) ? ok : ng) = mid;
    }
    return ok;
}
#include <algorithm>
#include <iterator>
#include <vector>
/// @brief 座標圧縮
template <class T>
struct coordinate_compression {
    coordinate_compression() = default;
    coordinate_compression(const std::vector<T> &_data) : data(_data) { build(); }
    const T &operator[](int i) const { return data[i]; }
    void add(T x) { data.emplace_back(x); }
    void build() {
        std::sort(data.begin(), data.end());
        data.erase(std::unique(data.begin(), data.end()), data.end());
    }
    bool exists(T x) const {
        auto it = std::lower_bound(data.begin(), data.end(), x);
        return it != data.end() && *it == x;
    }
    int get(T x) const {
        return std::distance(data.begin(), std::lower_bound(data.begin(), data.end(), x));
    }
    int lower_bound(T x) const {
        return std::distance(data.begin(), std::lower_bound(data.begin(), data.end(), x));
    }
    int upper_bound(T x) const {
        return std::distance(data.begin(), std::upper_bound(data.begin(), data.end(), x));
    }
    std::vector<int> compress(const std::vector<T> &v) const {
        int n = v.size();
        std::vector<int> res(n);
        for (int i = 0; i < n; ++i) res[i] = get(v[i]);
        return res;
    }
    int size() const { return data.size(); }
  private:
    std::vector<T> data;
};
/// @brief 座標圧縮
template <class T>
std::vector<int> compress(const std::vector<T> &v) {
    coordinate_compression cps(v);
    std::vector<int> res;
    res.reserve(std::size(v));
    for (auto &&x : v) res.emplace_back(cps.get(x));
    return res;
}
#include <cassert>
#include <tuple>
namespace internal {
/**
 * @brief 完備辞書
 *
 * @see https://ei1333.github.io/library/structure/wavelet/succinct-indexable-dictionary.hpp
 */
struct bit_vector {
    bit_vector() = default;
    bit_vector(unsigned int _length)
        : length(_length), blocks((_length + 31) >> 5), bit((_length + 31) >> 5),
          sum((_length + 31) >> 5) {}
    void set(unsigned int k) { bit[k >> 5] |= 1U << (k & 31); }
    void build() {
        sum[0] = 0U;
        for (unsigned int i = 1; i < blocks; ++i) {
            sum[i] = sum[i - 1] + __builtin_popcount(bit[i - 1]);
        }
    }
    bool operator[](unsigned int k) const { return bit[k >> 5] >> (k & 31) & 1; }
    unsigned int rank(unsigned int k) const {
        return sum[k >> 5] + __builtin_popcount(bit[k >> 5] & ((1U << (k & 31)) - 1));
    }
    unsigned int rank(bool val, unsigned int k) const { return val ? rank(k) : k - rank(k); }
    unsigned int select(unsigned int k) const {
        unsigned int sl = 0, sr = blocks + 1;
        while (sr - sl > 1) {
            unsigned int m = (sl + sr) >> 1;
            if (sum[m] < k) sl = m;
            else sr = m;
        }
        k -= sum[sl];
        unsigned int bl = 0, br = 32;
        while (br - bl > 1) {
            unsigned int m = (bl + br) >> 1;
            if (__builtin_popcount(bit[sl] & ((1U << m) - 1)) < k) bl = m;
            else br = m;
        }
        return (sl << 5) + bl;
    }
  private:
    unsigned int length, blocks;
    std::vector<unsigned int> bit, sum;
};
}  // namespace internal
/// @brief 重み付きウェーブレット行列
template <class T, class U = T, int L = 30>
struct wavelet_matrix_rectangle_sum {
    wavelet_matrix_rectangle_sum() = default;
    template <class Value>
    wavelet_matrix_rectangle_sum(const std::vector<T> &v, const std::vector<Value> &u)
        : length(v.size()) {
        assert(v.size() == u.size());
        std::vector<int> l(length), r(length), ord(length);
        std::iota(ord.begin(), ord.end(), 0);
        for (int level = L - 1; level >= 0; level--) {
            matrix[level] = internal::bit_vector(length + 1);
            int left = 0, right = 0;
            for (int i = 0; i < length; i++) {
                if ((v[ord[i]] >> level) & 1) {
                    matrix[level].set(i);
                    r[right++] = ord[i];
                } else {
                    l[left++] = ord[i];
                }
            }
            mid[level] = left;
            matrix[level].build();
            ord.swap(l);
            for (int i = 0; i < right; i++) ord[left + i] = r[i];
            cs[level].resize(length + 1);
            cs[level][0] = U(0);
            for (int i = 0; i < length; i++) cs[level][i + 1] = cs[level][i] + u[ord[i]];
        }
    }
    U kth_smallest_sum(int l, int r, int k) const {
        assert(0 <= k && k <= r - l);
        T val = T();
        U res = U();
        for (int level = L - 1; level >= 0; --level) {
            int cnt = matrix[level].rank(false, r) - matrix[level].rank(false, l);
            bool f = cnt <= k;
            if (f) {
                val |= T(1) << level;
                res += cs[level][matrix[level].rank(false, r)] -
                       cs[level][matrix[level].rank(false, l)];
                k -= cnt;
            }
            std::tie(l, r) = succ(f, l, r, level);
        }
        return res + val * k;
    }
    U kth_largest_sum(int l, int r, int k) const {
        return cs[L - 1][matrix[L - 1].rank(false, r)] + cs[L - 1][matrix[L - 1].rank(true, r)] -
               cs[L - 1][matrix[L - 1].rank(false, l)] - cs[L - 1][matrix[L - 1].rank(true, l)] -
               kth_smallest_sum(l, r, r - l - k);
    }
    U range_sum(int r, T x) const { return range_sum(0, r, x); }
    U range_sum(int l, int r, T x) const {
        for (int level = L - 1; level >= 0; level--)
            std::tie(l, r) = succ((x >> level) & 1, l, r, level);
        return cs[0][matrix[0].rank(false, r)] - cs[0][matrix[0].rank(false, l)];
    }
    U rect_sum(int l, int r, T upper) const {
        U res = 0;
        for (int level = L - 1; level >= 0; level--) {
            bool f = (upper >> level) & 1;
            if (f)
                res += cs[level][matrix[level].rank(false, r)] -
                       cs[level][matrix[level].rank(false, l)];
            std::tie(l, r) = succ(f, l, r, level);
        }
        return res;
    }
    U rect_sum(int l, int r, T lower, T upper) const {
        return rect_sum(l, r, upper) - rect_sum(l, r, lower);
    }
  private:
    int length;
    internal::bit_vector matrix[L];
    int mid[L];
    std::vector<U> cs[L];
    std::pair<int, int> succ(bool f, int l, int r, int level) const {
        return {matrix[level].rank(f, l) + mid[level] * f,
                matrix[level].rank(f, r) + mid[level] * f};
    }
};
#include <iostream>
/**
 * @brief 重み付きグラフ
 *
 * @tparam T 辺の重みの型
 */
template <class T>
struct Graph {
  private:
    struct _edge {
        constexpr _edge() : _from(), _to(), _weight() {}
        constexpr _edge(int from, int to, T weight) : _from(from), _to(to), _weight(weight) {}
        constexpr bool operator<(const _edge &rhs) const { return weight() < rhs.weight(); }
        constexpr bool operator>(const _edge &rhs) const { return rhs < *this; }
        constexpr int from() const { return _from; }
        constexpr int to() const { return _to; }
        constexpr T weight() const { return _weight; }
      private:
        int _from, _to;
        T _weight;
    };
  public:
    using edge_type = typename Graph<T>::_edge;
    Graph() : _size(), edges() {}
    Graph(int v) : _size(v), edges(v) {}
    const auto &operator[](int i) const { return edges[i]; }
    auto &operator[](int i) { return edges[i]; }
    const auto begin() const { return edges.begin(); }
    auto begin() { return edges.begin(); }
    const auto end() const { return edges.end(); }
    auto end() { return edges.end(); }
    constexpr int size() const { return _size; }
    void add_edge(const edge_type &e) { edges[e.from()].emplace_back(e); }
    void add_edge(int from, int to, T weight = T(1)) { edges[from].emplace_back(from, to, weight); }
    void add_edges(int from, int to, T weight = T(1)) {
        edges[from].emplace_back(from, to, weight);
        edges[to].emplace_back(to, from, weight);
    }
    void input_edge(int m, int base = 1) {
        for (int i = 0; i < m; ++i) {
            int from, to;
            T weight;
            std::cin >> from >> to >> weight;
            add_edge(from - base, to - base, weight);
        }
    }
    void input_edges(int m, int base = 1) {
        for (int i = 0; i < m; ++i) {
            int from, to;
            T weight;
            std::cin >> from >> to >> weight;
            add_edges(from - base, to - base, weight);
        }
    }
  private:
    int _size;
    std::vector<std::vector<edge_type>> edges;
};
template <>
struct Graph<void> {
  private:
    struct _edge {
        constexpr _edge() : _from(), _to() {}
        constexpr _edge(int from, int to) : _from(from), _to(to) {}
        constexpr int from() const { return _from; }
        constexpr int to() const { return _to; }
        constexpr int weight() const { return 1; }
        constexpr bool operator<(const _edge &rhs) const { return weight() < rhs.weight(); }
        constexpr bool operator>(const _edge &rhs) const { return rhs < *this; }
      private:
        int _from, _to;
    };
  public:
    using edge_type = typename Graph<void>::_edge;
    Graph() : _size(), edges() {}
    Graph(int v) : _size(v), edges(v) {}
    const auto &operator[](int i) const { return edges[i]; }
    auto &operator[](int i) { return edges[i]; }
    const auto begin() const { return edges.begin(); }
    auto begin() { return edges.begin(); }
    const auto end() const { return edges.end(); }
    auto end() { return edges.end(); }
    constexpr int size() const { return _size; }
    void add_edge(const edge_type &e) { edges[e.from()].emplace_back(e); }
    void add_edge(int from, int to) { edges[from].emplace_back(from, to); }
    void add_edges(int from, int to) {
        edges[from].emplace_back(from, to);
        edges[to].emplace_back(to, from);
    }
    void input_edge(int m, int base = 1) {
        for (int i = 0; i < m; ++i) {
            int from, to;
            std::cin >> from >> to;
            add_edge(from - base, to - base);
        }
    }
    void input_edges(int m, int base = 1) {
        for (int i = 0; i < m; ++i) {
            int from, to;
            std::cin >> from >> to;
            add_edges(from - base, to - base);
        }
    }
  private:
    int _size;
    std::vector<std::vector<edge_type>> edges;
};
#ifdef ATCODER
#pragma GCC target("sse4.2,avx512f,avx512dq,avx512ifma,avx512cd,avx512bw,avx512vl,bmi2")
#endif
#pragma GCC optimize("Ofast,fast-math,unroll-all-loops")
#include <bits/stdc++.h>
#ifndef ATCODER
#pragma GCC target("sse4.2,avx2,bmi2")
#endif
template <class T, class U>
constexpr bool chmax(T &a, const U &b) {
    return a < (T)b ? a = (T)b, true : false;
}
template <class T, class U>
constexpr bool chmin(T &a, const U &b) {
    return (T)b < a ? a = (T)b, true : false;
}
constexpr std::int64_t INF = 1000000000000000003;
constexpr int Inf = 1000000003;
constexpr double EPS = 1e-7;
constexpr double PI = 3.14159265358979323846;
#define FOR(i, m, n) for (int i = (m); i < int(n); ++i)
#define FORR(i, m, n) for (int i = (m)-1; i >= int(n); --i)
#define FORL(i, m, n) for (int64_t i = (m); i < int64_t(n); ++i)
#define rep(i, n) FOR (i, 0, n)
#define repn(i, n) FOR (i, 1, n + 1)
#define repr(i, n) FORR (i, n, 0)
#define repnr(i, n) FORR (i, n + 1, 1)
#define all(s) (s).begin(), (s).end()
struct Sonic {
    Sonic() {
        std::ios::sync_with_stdio(false);
        std::cin.tie(nullptr);
        std::cout << std::fixed << std::setprecision(20);
    }
    constexpr void operator()() const {}
} sonic;
using namespace std;
using ll = std::int64_t;
using ld = long double;
template <class T, class U>
std::istream &operator>>(std::istream &is, std::pair<T, U> &p) {
    return is >> p.first >> p.second;
}
template <class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
    for (T &i : v) is >> i;
    return is;
}
template <class T, class U>
std::ostream &operator<<(std::ostream &os, const std::pair<T, U> &p) {
    return os << '(' << p.first << ',' << p.second << ')';
}
template <class T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
    for (auto it = v.begin(); it != v.end(); ++it) os << (it == v.begin() ? "" : " ") << *it;
    return os;
}
template <class Head, class... Tail>
void co(Head &&head, Tail &&...tail) {
    if constexpr (sizeof...(tail) == 0) std::cout << head << '\n';
    else std::cout << head << ' ', co(std::forward<Tail>(tail)...);
}
template <class Head, class... Tail>
void ce(Head &&head, Tail &&...tail) {
    if constexpr (sizeof...(tail) == 0) std::cerr << head << '\n';
    else std::cerr << head << ' ', ce(std::forward<Tail>(tail)...);
}
void Yes(bool is_correct = true) { std::cout << (is_correct ? "Yes\n" : "No\n"); }
void No(bool is_not_correct = true) { Yes(!is_not_correct); }
void YES(bool is_correct = true) { std::cout << (is_correct ? "YES\n" : "NO\n"); }
void NO(bool is_not_correct = true) { YES(!is_not_correct); }
void Takahashi(bool is_correct = true) { std::cout << (is_correct ? "Takahashi" : "Aoki") << '\n'; }
void Aoki(bool is_not_correct = true) { Takahashi(!is_not_correct); }
#include <bit>
/// @brief スパーステーブル
template <class M>
struct sparse_table {
  private:
    using T = typename M::value_type;
  public:
    sparse_table() = default;
    sparse_table(const std::vector<T> &v) : _size(v.size()), data() {
        int b = std::max(1, std::countr_zero(std::bit_ceil<unsigned>(_size)));
        data.emplace_back(v);
        for (int i = 1; i < b; ++i) data.emplace_back(_size + 1 - (1 << i));
        for (int i = 1; i < b; ++i) {
            for (int j = 0; j + (1 << i) <= _size; ++j) {
                data[i][j] = M::op(data[i - 1][j], data[i - 1][j + (1 << (i - 1))]);
            }
        }
    }
    T prod(int l, int r) const {
        assert(0 <= l && l <= r && r <= _size);
        if (l == r) return M::id();
        if (l + 1 == r) return data[0][l];
        int b = 31 - std::countl_zero<unsigned>(r - l - 1);
        return M::op(data[b][l], data[b][r - (1 << b)]);
    }
  private:
    int _size;
    std::vector<std::vector<T>> data;
};
namespace internal {
template <class T, int N>
struct fixed_stack {
    constexpr fixed_stack() : _size(), _data() {}
    constexpr T top() const { return _data[_size - 1]; }
    constexpr bool empty() const { return _size == 0; }
    constexpr int size() const { return _size; }
    constexpr void emplace(const T &e) { _data[_size++] = e; }
    constexpr void emplace(T &&e) { _data[_size++] = e; }
    constexpr void pop() { --_size; }
    constexpr void clear() { _size = 0; }
  private:
    int _size;
    std::array<T, N> _data;
};
}  // namespace internal
/**
 * @brief 線形 Sparse Table
 *
 * @tparam M
 */
template <class M>
struct linear_sparse_table {
  private:
    using T = M::value_type;
    static constexpr int W = 64;
  public:
    linear_sparse_table() = default;
    linear_sparse_table(const std::vector<T> &v) : _size(v.size()), data(v) {
        int n = v.size();
        int b = n / W;
        internal::fixed_stack<int, W + 1> st;
        std::vector<T> u(b);
        word_data.resize(b + (n > b * W));
        for (int i = 0; i < b; ++i) {
            T m = M::id();
            std::uint64_t bit = 0;
            std::vector<std::uint64_t> bits(W);
            for (int j = 0; j < W; ++j) {
                m = M::op(m, v[i * W + j]);
                while (!st.empty() && M::op(v[i * W + st.top()], v[i * W + j]) == v[i * W + j]) {
                    bit ^= std::uint64_t(1) << st.top();
                    st.pop();
                }
                bits[j] = bit;
                bit |= std::uint64_t(1) << j;
                st.emplace(j);
            }
            u[i] = m;
            word_data[i] = bits;
            st.clear();
        }
        if (n > b * W) {
            std::uint64_t bit = 0;
            std::vector<std::uint64_t> bits(n - b * W);
            for (int j = 0; j < n - b * W; ++j) {
                while (!st.empty() && M::op(v[b * W + st.top()], v[b * W + j]) == v[b * W + j]) {
                    bit ^= std::uint64_t(1) << st.top();
                    st.pop();
                }
                bits[j] = bit;
                bit |= std::uint64_t(1) << j;
                st.emplace(j);
            }
            word_data[b] = bits;
        }
        block_table = sparse_table<M>(u);
    }
    const T &operator[](int k) const { return data[k]; }
    T prod(int l, int r) const {
        assert(0 <= l && l < r && r <= _size);
        int lb = (l + W - 1) / W, rb = r / W;
        if (lb > rb) return word_prod(l, r);
        T res = (lb == rb ? M::id() : block_table.prod(lb, rb));
        if (l < lb * W) res = M::op(res, word_prod(l, lb * W));
        if (rb * W < r) res = M::op(res, word_prod(rb * W, r));
        return res;
    }
  private:
    int _size;
    std::vector<T> data;
    sparse_table<M> block_table;
    std::vector<std::vector<std::uint64_t>> word_data;
    T word_prod(int l, int r) const {
        if (l == r) return M::id();
        int b = l / W;
        int lw = l - b * W, rw = r - b * W;
        if ((word_data[b][rw - 1] >> lw) == 0ul) return data[r - 1];
        return data[l + std::countr_zero(word_data[b][rw - 1] >> lw)];
    }
};
struct linear_lca {
  private:
    struct S {
        int depth, index;
        bool operator<(const S &rhs) const { return depth < rhs.depth; }
        bool operator==(const S &rhs) const = default;
    };
    struct M {
        using value_type = S;
        static constexpr S id() { return S{std::numeric_limits<int>::max(), -1}; }
        static constexpr S op(const S &lhs, const S &rhs) { return std::min(lhs, rhs); }
    };
  public:
    template <class T>
    linear_lca(const Graph<T> &g, int r = 0) : ord(g.size(), -1), lst() {
        std::vector<S> v;
        std::stack<std::pair<int, int>> st;
        st.emplace(r, 0);
        while (!st.empty()) {
            auto [x, d] = st.top();
            st.pop();
            if (x < 0) {
                v.emplace_back(d, ~x);
                continue;
            }
            ord[x] = v.size();
            v.emplace_back(d, x);
            for (auto e : g[x]) {
                if (ord[e.to()] != -1) continue;
                st.emplace(~x, d);
                st.emplace(e.to(), d + 1);
            }
        }
        lst = linear_sparse_table<M>(v);
    }
    int operator()(int u, int v) const { return lca(u, v); }
    int lca(int u, int v) const {
        auto [l, r] = std::minmax(ord[u], ord[v]);
        return lst.prod(l, r + 1).index;
    }
  private:
    std::vector<int> ord;
    linear_sparse_table<M> lst;
};
int main(void) {
    int n, q;
    cin >> n >> q;
    vector<int> a(n);
    cin >> a;
    coordinate_compression cps(a);
    a = cps.compress(a);
    Graph<void> g(n);
    g.input_edges(n - 1);
    vector<int> in(n), out(n), ord(n * 2), w(n * 2);
    int c = 0;
    auto dfs = [&](auto self, int x, int p) -> void {
        in[x] = c;
        w[c] = 1;
        ord[c++] = a[x];
        for (auto e : g[x]) {
            if (e.to() == p)
                continue;
            self(self, e.to(), x);
        }
        w[c] = -1;
        ord[c++] = a[x];
        out[x] = c;
    };
    dfs(dfs, 0, -1);
    linear_lca lca(g);
    wavelet_matrix_rectangle_sum wm(ord, w);
    while (q--) {
        int u, v, k;
        cin >> u >> v >> k;
        --u, --v;
        auto f = [&](int y) {
            return wm.rect_sum(0, in[u] + 1, y) + wm.rect_sum(0, in[v] + 1, y) -
                       wm.rect_sum(0, in[lca(u, v)] + 1, y) - wm.rect_sum(0, in[lca(u, v)], y) <
                   k;
        };
        auto ans = meguru_binary_search(0, cps.size() + 1, f);
        co(cps[ans]);
    }
    return 0;
}
accuracy: 1
time_ms: 1250
memory_kb: 49536
final_rank: 5

code_snippet: // competitive-verifier: PROBLEM
#include <cmath>
#include <cstdint>
#include <numeric>
/// @brief めぐる式二分探索
template <class T, class F>
std::int64_t meguru_binary_search(T ok, T ng, F check) {
    while (std::abs(ok - ng) > 1) {
        T mid = std::midpoint(ok, ng);
        (check(mid) ? ok : ng) = mid;
    }
    return ok;
}
#include <cassert>
#include <tuple>
#include <vector>
namespace internal {
/**
 * @brief 完備辞書
 *
 * @see https://ei1333.github.io/library/structure/wavelet/succinct-indexable-dictionary.hpp
 */
struct bit_vector {
    bit_vector() = default;
    bit_vector(unsigned int _length)
        : length(_length), blocks((_length + 31) >> 5), bit((_length + 31) >> 5),
          sum((_length + 31) >> 5) {}
    void set(unsigned int k) { bit[k >> 5] |= 1U << (k & 31); }
    void build() {
        sum[0] = 0U;
        for (unsigned int i = 1; i < blocks; ++i) {
            sum[i] = sum[i - 1] + __builtin_popcount(bit[i - 1]);
        }
    }
    bool operator[](unsigned int k) const { return bit[k >> 5] >> (k & 31) & 1; }
    unsigned int rank(unsigned int k) const {
        return sum[k >> 5] + __builtin_popcount(bit[k >> 5] & ((1U << (k & 31)) - 1));
    }
    unsigned int rank(bool val, unsigned int k) const { return val ? rank(k) : k - rank(k); }
    unsigned int select(unsigned int k) const {
        unsigned int sl = 0, sr = blocks + 1;
        while (sr - sl > 1) {
            unsigned int m = (sl + sr) >> 1;
            if (sum[m] < k) sl = m;
            else sr = m;
        }
        k -= sum[sl];
        unsigned int bl = 0, br = 32;
        while (br - bl > 1) {
            unsigned int m = (bl + br) >> 1;
            if (__builtin_popcount(bit[sl] & ((1U << m) - 1)) < k) bl = m;
            else br = m;
        }
        return (sl << 5) + bl;
    }
  private:
    unsigned int length, blocks;
    std::vector<unsigned int> bit, sum;
};
}  // namespace internal
/// @brief 重み付きウェーブレット行列
template <class T, class U = T, int L = 30>
struct wavelet_matrix_rectangle_sum {
    wavelet_matrix_rectangle_sum() = default;
    template <class Value>
    wavelet_matrix_rectangle_sum(const std::vector<T> &v, const std::vector<Value> &u)
        : length(v.size()) {
        assert(v.size() == u.size());
        std::vector<int> l(length), r(length), ord(length);
        std::iota(ord.begin(), ord.end(), 0);
        for (int level = L - 1; level >= 0; level--) {
            matrix[level] = internal::bit_vector(length + 1);
            int left = 0, right = 0;
            for (int i = 0; i < length; i++) {
                if ((v[ord[i]] >> level) & 1) {
                    matrix[level].set(i);
                    r[right++] = ord[i];
                } else {
                    l[left++] = ord[i];
                }
            }
            mid[level] = left;
            matrix[level].build();
            ord.swap(l);
            for (int i = 0; i < right; i++) ord[left + i] = r[i];
            cs[level].resize(length + 1);
            cs[level][0] = U(0);
            for (int i = 0; i < length; i++) cs[level][i + 1] = cs[level][i] + u[ord[i]];
        }
    }
    U kth_smallest_sum(int l, int r, int k) const {
        assert(0 <= k && k <= r - l);
        T val = T();
        U res = U();
        for (int level = L - 1; level >= 0; --level) {
            int cnt = matrix[level].rank(false, r) - matrix[level].rank(false, l);
            bool f = cnt <= k;
            if (f) {
                val |= T(1) << level;
                res += cs[level][matrix[level].rank(false, r)] -
                       cs[level][matrix[level].rank(false, l)];
                k -= cnt;
            }
            std::tie(l, r) = succ(f, l, r, level);
        }
        return res + val * k;
    }
    U kth_largest_sum(int l, int r, int k) const {
        return cs[L - 1][matrix[L - 1].rank(false, r)] + cs[L - 1][matrix[L - 1].rank(true, r)] -
               cs[L - 1][matrix[L - 1].rank(false, l)] - cs[L - 1][matrix[L - 1].rank(true, l)] -
               kth_smallest_sum(l, r, r - l - k);
    }
    U range_sum(int r, T x) const { return range_sum(0, r, x); }
    U range_sum(int l, int r, T x) const {
        for (int level = L - 1; level >= 0; level--)
            std::tie(l, r) = succ((x >> level) & 1, l, r, level);
        return cs[0][matrix[0].rank(false, r)] - cs[0][matrix[0].rank(false, l)];
    }
    U rect_sum(int l, int r, T upper) const {
        U res = 0;
        for (int level = L - 1; level >= 0; level--) {
            bool f = (upper >> level) & 1;
            if (f)
                res += cs[level][matrix[level].rank(false, r)] -
                       cs[level][matrix[level].rank(false, l)];
            std::tie(l, r) = succ(f, l, r, level);
        }
        return res;
    }
    U rect_sum(int l, int r, T lower, T upper) const {
        return rect_sum(l, r, upper) - rect_sum(l, r, lower);
    }
  private:
    int length;
    internal::bit_vector matrix[L];
    int mid[L];
    std::vector<U> cs[L];
    std::pair<int, int> succ(bool f, int l, int r, int level) const {
        return {matrix[level].rank(f, l) + mid[level] * f,
                matrix[level].rank(f, r) + mid[level] * f};
    }
};
#include <iostream>
/**
 * @brief 重み付きグラフ
 *
 * @tparam T 辺の重みの型
 */
template <class T>
struct Graph {
  private:
    struct _edge {
        constexpr _edge() : _from(), _to(), _weight() {}
        constexpr _edge(int from, int to, T weight) : _from(from), _to(to), _weight(weight) {}
        constexpr bool operator<(const _edge &rhs) const { return weight() < rhs.weight(); }
        constexpr bool operator>(const _edge &rhs) const { return rhs < *this; }
        constexpr int from() const { return _from; }
        constexpr int to() const { return _to; }
        constexpr T weight() const { return _weight; }
      private:
        int _from, _to;
        T _weight;
    };
  public:
    using edge_type = typename Graph<T>::_edge;
    Graph() : _size(), edges() {}
    Graph(int v) : _size(v), edges(v) {}
    const auto &operator[](int i) const { return edges[i]; }
    auto &operator[](int i) { return edges[i]; }
    const auto begin() const { return edges.begin(); }
    auto begin() { return edges.begin(); }
    const auto end() const { return edges.end(); }
    auto end() { return edges.end(); }
    constexpr int size() const { return _size; }
    void add_edge(const edge_type &e) { edges[e.from()].emplace_back(e); }
    void add_edge(int from, int to, T weight = T(1)) { edges[from].emplace_back(from, to, weight); }
    void add_edges(int from, int to, T weight = T(1)) {
        edges[from].emplace_back(from, to, weight);
        edges[to].emplace_back(to, from, weight);
    }
    void input_edge(int m, int base = 1) {
        for (int i = 0; i < m; ++i) {
            int from, to;
            T weight;
            std::cin >> from >> to >> weight;
            add_edge(from - base, to - base, weight);
        }
    }
    void input_edges(int m, int base = 1) {
        for (int i = 0; i < m; ++i) {
            int from, to;
            T weight;
            std::cin >> from >> to >> weight;
            add_edges(from - base, to - base, weight);
        }
    }
  private:
    int _size;
    std::vector<std::vector<edge_type>> edges;
};
template <>
struct Graph<void> {
  private:
    struct _edge {
        constexpr _edge() : _from(), _to() {}
        constexpr _edge(int from, int to) : _from(from), _to(to) {}
        constexpr int from() const { return _from; }
        constexpr int to() const { return _to; }
        constexpr int weight() const { return 1; }
        constexpr bool operator<(const _edge &rhs) const { return weight() < rhs.weight(); }
        constexpr bool operator>(const _edge &rhs) const { return rhs < *this; }
      private:
        int _from, _to;
    };
  public:
    using edge_type = typename Graph<void>::_edge;
    Graph() : _size(), edges() {}
    Graph(int v) : _size(v), edges(v) {}
    const auto &operator[](int i) const { return edges[i]; }
    auto &operator[](int i) { return edges[i]; }
    const auto begin() const { return edges.begin(); }
    auto begin() { return edges.begin(); }
    const auto end() const { return edges.end(); }
    auto end() { return edges.end(); }
    constexpr int size() const { return _size; }
    void add_edge(const edge_type &e) { edges[e.from()].emplace_back(e); }
    void add_edge(int from, int to) { edges[from].emplace_back(from, to); }
    void add_edges(int from, int to) {
        edges[from].emplace_back(from, to);
        edges[to].emplace_back(to, from);
    }
    void input_edge(int m, int base = 1) {
        for (int i = 0; i < m; ++i) {
            int from, to;
            std::cin >> from >> to;
            add_edge(from - base, to - base);
        }
    }
    void input_edges(int m, int base = 1) {
        for (int i = 0; i < m; ++i) {
            int from, to;
            std::cin >> from >> to;
            add_edges(from - base, to - base);
        }
    }
  private:
    int _size;
    std::vector<std::vector<edge_type>> edges;
};
#ifdef ATCODER
#pragma GCC target("sse4.2,avx512f,avx512dq,avx512ifma,avx512cd,avx512bw,avx512vl,bmi2")
#endif
#pragma GCC optimize("Ofast,fast-math,unroll-all-loops")
#include <bits/stdc++.h>
#ifndef ATCODER
#pragma GCC target("sse4.2,avx2,bmi2")
#endif
template <class T, class U>
constexpr bool chmax(T &a, const U &b) {
    return a < (T)b ? a = (T)b, true : false;
}
template <class T, class U>
constexpr bool chmin(T &a, const U &b) {
    return (T)b < a ? a = (T)b, true : false;
}
constexpr std::int64_t INF = 1000000000000000003;
constexpr int Inf = 1000000003;
constexpr double EPS = 1e-7;
constexpr double PI = 3.14159265358979323846;
#define FOR(i, m, n) for (int i = (m); i < int(n); ++i)
#define FORR(i, m, n) for (int i = (m)-1; i >= int(n); --i)
#define FORL(i, m, n) for (int64_t i = (m); i < int64_t(n); ++i)
#define rep(i, n) FOR (i, 0, n)
#define repn(i, n) FOR (i, 1, n + 1)
#define repr(i, n) FORR (i, n, 0)
#define repnr(i, n) FORR (i, n + 1, 1)
#define all(s) (s).begin(), (s).end()
struct Sonic {
    Sonic() {
        std::ios::sync_with_stdio(false);
        std::cin.tie(nullptr);
        std::cout << std::fixed << std::setprecision(20);
    }
    constexpr void operator()() const {}
} sonic;
using namespace std;
using ll = std::int64_t;
using ld = long double;
template <class T, class U>
std::istream &operator>>(std::istream &is, std::pair<T, U> &p) {
    return is >> p.first >> p.second;
}
template <class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
    for (T &i : v) is >> i;
    return is;
}
template <class T, class U>
std::ostream &operator<<(std::ostream &os, const std::pair<T, U> &p) {
    return os << '(' << p.first << ',' << p.second << ')';
}
template <class T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
    for (auto it = v.begin(); it != v.end(); ++it) os << (it == v.begin() ? "" : " ") << *it;
    return os;
}
template <class Head, class... Tail>
void co(Head &&head, Tail &&...tail) {
    if constexpr (sizeof...(tail) == 0) std::cout << head << '\n';
    else std::cout << head << ' ', co(std::forward<Tail>(tail)...);
}
template <class Head, class... Tail>
void ce(Head &&head, Tail &&...tail) {
    if constexpr (sizeof...(tail) == 0) std::cerr << head << '\n';
    else std::cerr << head << ' ', ce(std::forward<Tail>(tail)...);
}
void Yes(bool is_correct = true) { std::cout << (is_correct ? "Yes\n" : "No\n"); }
void No(bool is_not_correct = true) { Yes(!is_not_correct); }
void YES(bool is_correct = true) { std::cout << (is_correct ? "YES\n" : "NO\n"); }
void NO(bool is_not_correct = true) { YES(!is_not_correct); }
void Takahashi(bool is_correct = true) { std::cout << (is_correct ? "Takahashi" : "Aoki") << '\n'; }
void Aoki(bool is_not_correct = true) { Takahashi(!is_not_correct); }
#include <bit>
/// @brief スパーステーブル
template <class M>
struct sparse_table {
  private:
    using T = typename M::value_type;
  public:
    sparse_table() = default;
    sparse_table(const std::vector<T> &v) : _size(v.size()), data() {
        int b = std::max(1, std::countr_zero(std::bit_ceil<unsigned>(_size)));
        data.emplace_back(v);
        for (int i = 1; i < b; ++i) data.emplace_back(_size + 1 - (1 << i));
        for (int i = 1; i < b; ++i) {
            for (int j = 0; j + (1 << i) <= _size; ++j) {
                data[i][j] = M::op(data[i - 1][j], data[i - 1][j + (1 << (i - 1))]);
            }
        }
    }
    T prod(int l, int r) const {
        assert(0 <= l && l <= r && r <= _size);
        if (l == r) return M::id();
        if (l + 1 == r) return data[0][l];
        int b = 31 - std::countl_zero<unsigned>(r - l - 1);
        return M::op(data[b][l], data[b][r - (1 << b)]);
    }
  private:
    int _size;
    std::vector<std::vector<T>> data;
};
namespace internal {
template <class T, int N>
struct fixed_stack {
    constexpr fixed_stack() : _size(), _data() {}
    constexpr T top() const { return _data[_size - 1]; }
    constexpr bool empty() const { return _size == 0; }
    constexpr int size() const { return _size; }
    constexpr void emplace(const T &e) { _data[_size++] = e; }
    constexpr void emplace(T &&e) { _data[_size++] = e; }
    constexpr void pop() { --_size; }
    constexpr void clear() { _size = 0; }
  private:
    int _size;
    std::array<T, N> _data;
};
}  // namespace internal
/**
 * @brief 線形 Sparse Table
 *
 * @tparam M
 */
template <class M>
struct linear_sparse_table {
  private:
    using T = M::value_type;
    static constexpr int W = 64;
  public:
    linear_sparse_table() = default;
    linear_sparse_table(const std::vector<T> &v) : _size(v.size()), data(v) {
        int n = v.size();
        int b = n / W;
        internal::fixed_stack<int, W + 1> st;
        std::vector<T> u(b);
        word_data.resize(b + (n > b * W));
        for (int i = 0; i < b; ++i) {
            T m = M::id();
            std::uint64_t bit = 0;
            std::vector<std::uint64_t> bits(W);
            for (int j = 0; j < W; ++j) {
                m = M::op(m, v[i * W + j]);
                while (!st.empty() && M::op(v[i * W + st.top()], v[i * W + j]) == v[i * W + j]) {
                    bit ^= std::uint64_t(1) << st.top();
                    st.pop();
                }
                bits[j] = bit;
                bit |= std::uint64_t(1) << j;
                st.emplace(j);
            }
            u[i] = m;
            word_data[i] = bits;
            st.clear();
        }
        if (n > b * W) {
            std::uint64_t bit = 0;
            std::vector<std::uint64_t> bits(n - b * W);
            for (int j = 0; j < n - b * W; ++j) {
                while (!st.empty() && M::op(v[b * W + st.top()], v[b * W + j]) == v[b * W + j]) {
                    bit ^= std::uint64_t(1) << st.top();
                    st.pop();
                }
                bits[j] = bit;
                bit |= std::uint64_t(1) << j;
                st.emplace(j);
            }
            word_data[b] = bits;
        }
        block_table = sparse_table<M>(u);
    }
    const T &operator[](int k) const { return data[k]; }
    T prod(int l, int r) const {
        assert(0 <= l && l < r && r <= _size);
        int lb = (l + W - 1) / W, rb = r / W;
        if (lb > rb) return word_prod(l, r);
        T res = (lb == rb ? M::id() : block_table.prod(lb, rb));
        if (l < lb * W) res = M::op(res, word_prod(l, lb * W));
        if (rb * W < r) res = M::op(res, word_prod(rb * W, r));
        return res;
    }
  private:
    int _size;
    std::vector<T> data;
    sparse_table<M> block_table;
    std::vector<std::vector<std::uint64_t>> word_data;
    T word_prod(int l, int r) const {
        if (l == r) return M::id();
        int b = l / W;
        int lw = l - b * W, rw = r - b * W;
        if ((word_data[b][rw - 1] >> lw) == 0ul) return data[r - 1];
        return data[l + std::countr_zero(word_data[b][rw - 1] >> lw)];
    }
};
struct linear_lca {
  private:
    struct S {
        int depth, index;
        bool operator<(const S &rhs) const { return depth < rhs.depth; }
        bool operator==(const S &rhs) const = default;
    };
    struct M {
        using value_type = S;
        static constexpr S id() { return S{std::numeric_limits<int>::max(), -1}; }
        static constexpr S op(const S &lhs, const S &rhs) { return std::min(lhs, rhs); }
    };
  public:
    template <class T>
    linear_lca(const Graph<T> &g, int r = 0) : ord(g.size(), -1), lst() {
        std::vector<S> v;
        std::stack<std::pair<int, int>> st;
        st.emplace(r, 0);
        while (!st.empty()) {
            auto [x, d] = st.top();
            st.pop();
            if (x < 0) {
                v.emplace_back(d, ~x);
                continue;
            }
            ord[x] = v.size();
            v.emplace_back(d, x);
            for (auto e : g[x]) {
                if (ord[e.to()] != -1) continue;
                st.emplace(~x, d);
                st.emplace(e.to(), d + 1);
            }
        }
        lst = linear_sparse_table<M>(v);
    }
    int operator()(int u, int v) const { return lca(u, v); }
    int lca(int u, int v) const {
        auto [l, r] = std::minmax(ord[u], ord[v]);
        return lst.prod(l, r + 1).index;
    }
  private:
    std::vector<int> ord;
    linear_sparse_table<M> lst;
};
int main(void) {
    int n, q;
    cin >> n >> q;
    vector<int> a(n);
    cin >> a;
    Graph<void> g(n);
    g.input_edges(n - 1);
    vector<int> in(n), out(n), ord(n * 2), w(n * 2);
    int c = 0;
    auto dfs = [&](auto self, int x, int p) -> void {
        in[x] = c;
        w[c] = 1;
        ord[c++] = a[x];
        for (auto e : g[x]) {
            if (e.to() == p)
                continue;
            self(self, e.to(), x);
        }
        w[c] = -1;
        ord[c++] = a[x];
        out[x] = c;
    };
    dfs(dfs, 0, -1);
    linear_lca lca(g);
    wavelet_matrix_rectangle_sum wm(ord, w);
    while (q--) {
        int u, v, k;
        cin >> u >> v >> k;
        --u, --v;
        auto f = [&](int y) {
            return wm.rect_sum(0, in[u] + 1, y) + wm.rect_sum(0, in[v] + 1, y) -
                       wm.rect_sum(0, in[lca(u, v)] + 1, y) - wm.rect_sum(0, in[lca(u, v)], y) <
                   k;
        };
        auto ans = meguru_binary_search(0, *max_element(all(a)) + 1, f);
        co(ans);
    }
    return 0;
}
accuracy: 1
time_ms: 2810
memory_kb: 49140
final_rank: 14

code_snippet: // AOJ #2270 The L-th Number
// 2025.3.17

#include <bits/stdc++.h>
using namespace std;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {  // 整数の入力
	int n = 0; int c = gc();
	if (c == '-') {	c = gc();
		do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
		return -n;
	}
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

void Cout(int n) { // 整数の表示（出力）
	char b[30];
	if (!n) pc('0');
	else {
		if (n < 0) pc('-'), n = -n;
		int i = 0; while (n) b[i++] = n % 10 + '0', n /= 10;
		while (i--) pc(b[i]);
	}
	pc('\n');
}

struct Node { int value, left, right; };
const int MAXN = 100005;
Node segTree[MAXN * 20];
int segRoot[MAXN];
int segTreeCount = 0;

int modify(int lBound, int rBound, int pos, int previousNode) {
    int currentNode = ++segTreeCount;
    segTree[currentNode] = segTree[previousNode];
    segTree[currentNode].value++;

    if (lBound == rBound) return currentNode;

    int mid = (lBound + rBound) >> 1;
    if (pos <= mid) {
        segTree[currentNode].left = modify(lBound, mid, pos, segTree[previousNode].left);
    } else {
        segTree[currentNode].right = modify(mid + 1, rBound, pos, segTree[previousNode].right);
    }
    return currentNode;
}

int query(int lBound, int rBound, int kth, int nodeU, int nodeV, int nodeLCA, int nodeParentLCA) {
    if (lBound == rBound) return lBound;
    int mid = (lBound + rBound) >> 1;
    int countLeft = segTree[segTree[nodeU].left].value
                  + segTree[segTree[nodeV].left].value
                  - segTree[segTree[nodeLCA].left].value
                  - segTree[segTree[nodeParentLCA].left].value;
    if (kth <= countLeft)
        return query(lBound, mid, kth,
                     segTree[nodeU].left, segTree[nodeV].left,
                     segTree[nodeLCA].left, segTree[nodeParentLCA].left);
    else return query(mid + 1, rBound, kth - countLeft,
                     segTree[nodeU].right, segTree[nodeV].right,
                     segTree[nodeLCA].right, segTree[nodeParentLCA].right);
}

int totEdges = 0;
int head[MAXN], to[MAXN << 1], nxt[MAXN << 1];

void addEdge(int u, int v) {
    to[++totEdges] = v;
    nxt[totEdges] = head[u];
    head[u] = totEdges;
}

int totDFS = 0;
int parent[MAXN], depth[MAXN], top[MAXN], subTreeSize[MAXN], id[MAXN], heavySon[MAXN];
int n, q, totValues;
int value[MAXN], sortedValues[MAXN];

void dfs1(int u) {
    subTreeSize[u] = 0;
    for (int edge = head[u]; edge; edge = nxt[edge]) {
        int v = to[edge];
        if (v == parent[u]) continue;
        parent[v] = u;
        depth[v] = depth[u] + 1;
        dfs1(v);
        subTreeSize[u] += subTreeSize[v];
        if (subTreeSize[v] > subTreeSize[heavySon[u]]) heavySon[u] = v;
    }
    subTreeSize[u]++;
}

void dfs2(int u, int topNode) {
    segRoot[u] = modify(1, totValues, value[u], segRoot[parent[u]]);
    id[u] = ++totDFS;
    top[u] = topNode;
    if (heavySon[u]) dfs2(heavySon[u], topNode);
    for (int edge = head[u]; edge; edge = nxt[edge]) {
        int v = to[edge];
        if (v == parent[u] || v == heavySon[u]) continue;
        dfs2(v, v);
    }
}

int lca(int u, int v) {
    while (top[u] != top[v]) {
        if (depth[top[u]] < depth[top[v]]) swap(u, v);
        u = parent[top[u]];
    }
    return depth[u] < depth[v] ? u : v;
}

int main() {
    n = Cin(), q = Cin();

    for (int i = 1; i <= n; i++) sortedValues[i] = value[i] = Cin();

    for (int i = 2; i <= n; i++) {
        int u = Cin(), v = Cin();
        addEdge(u, v);
        addEdge(v, u);
    }

    sort(sortedValues + 1, sortedValues + n + 1);
    totValues = unique(sortedValues + 1, sortedValues + n + 1) - (sortedValues + 1);
    for (int i = 1; i <= n; i++)
        value[i] = lower_bound(sortedValues + 1, sortedValues + totValues + 1, value[i]) - sortedValues;

    depth[1] = 1;
    dfs1(1);
    dfs2(1, 1);

    while (q--) {
        int u = Cin(), v = Cin(), kth = Cin();
        int commonAncestor = lca(u, v);
        int index = query(1, totValues, kth, segRoot[u], segRoot[v],
                          segRoot[commonAncestor], segRoot[parent[commonAncestor]]);
        Cout(sortedValues[index]);
    }
    return 0;
}
accuracy: 1
time_ms: 90
memory_kb: 32800
final_rank: 1

code_snippet: #include <bits/stdc++.h>
using namespace std;
#if __has_include(<atcoder/all>)
#include <atcoder/all>
#endif
using ll=long long;
using ull=unsigned long long;
using P=pair<ll,ll>;
template<typename T>using minque=priority_queue<T,vector<T>,greater<T>>;
template<typename T>bool chmax(T &a,const T &b){return (a<b?(a=b,true):false);}
template<typename T>bool chmin(T &a,const T &b){return (a>b?(a=b,true):false);}
template<typename T1,typename T2>istream &operator>>(istream &is,pair<T1,T2>&p){is>>p.first>>p.second;return is;}
template<typename T>istream &operator>>(istream &is,vector<T> &a){for(auto &i:a)is>>i;return is;}
template<typename T1,typename T2>void operator++(pair<T1,T2>&a,int n){a.first++,a.second++;}
template<typename T1,typename T2>void operator--(pair<T1,T2>&a,int n){a.first--,a.second--;}
template<typename T>void operator++(vector<T>&a,int n){for(auto &i:a)i++;}
template<typename T>void operator--(vector<T>&a,int n){for(auto &i:a)i--;}
#define reps(i,a,n) for(int i=(a);i<int(n);i++)
#define rep(i,n) reps(i,0,n)
#define all(x) x.begin(),x.end()
#define pcnt(x) __builtin_popcountll(x)
#define fin(x) return cout<<x<<'\n',static_cast<void>(0)
ll myceil(ll a,ll b){return (a+b-1)/b;}
template<typename T,size_t n,size_t id=0>
auto vec(const int (&d)[n],const T &init=T()){
  if constexpr (id<n)return vector(d[id],vec<T,n,id+1>(d,init));
  else return init;
}
#ifdef LOCAL
#include<debug.h>
#else
#define debug(...) static_cast<void>(0)
#define debugg(...) static_cast<void>(0)
template<typename T1,typename T2>ostream &operator<<(ostream &os,const pair<T1,T2>&p){os<<p.first<<' '<<p.second;return os;}
#endif
void SOLVE();
int main(){
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cout<<fixed<<setprecision(16);
  #ifdef LOCAL
  clock_t start=clock();
  #endif
  int testcase=1;
  //cin>>testcase;
  for(int i=0;i<testcase;i++){
    SOLVE();
  }
  #ifdef LOCAL
  cerr<<"time:";
  cerr<<(clock()-start)/1000;
  cerr<<"ms\n";
  #endif
}
using namespace std;
struct UnionFind{
private:
  vector<int>par;
  int cs;
public:
  UnionFind(int n):par(n,-1),cs(n){}
  UnionFind(){}
  int root(int u){
    if(par[u]<0)return u;
    return par[u]=root(par[u]);
  }
  bool merge(int u,int v){
    int ru=root(u),rv=root(v);
    if(ru==rv)return false;
    if(par[ru]<par[rv])swap(ru,rv);
    par[rv]+=par[ru];
    par[ru]=rv;
    cs--;
    return true;
  }
  bool same(int u,int v){return root(u)==root(v);}
  int size(int u=-1){return u==-1?cs:-par[root(u)];}
};
template<typename T=int>
struct Edge{
  int from,to;
  T weight;
  int index;
  Edge(int from_,int to_,T weight_=T(),int index_=-1):from(from_),to(to_),weight(weight_),index(index_){}
  Edge():from(-1),to(-1),weight(),index(-1){}
  friend ostream &operator<<(ostream &os,const Edge&e){
    os<<'[';
    os<<"from:"<<e.from;
    os<<"to:"<<e.to;
    os<<"weight:"<<e.weight;
    os<<"index:"<<e.index;
    os<<']';
    return os;
  }
};
using namespace std;
template<typename T=int>
struct Tree{
private:
  int n;
  vector<Edge<T>>edge;
  vector<Edge<T>>g;
  vector<int>ptr;
  struct tree_range{
    using iterator=typename vector<Edge<T>>::iterator;
    iterator l,r;
    iterator begin()const{return l;}
    iterator end()const{return r;}
    int size()const{return r-l;}
    bool empty()const{return !size();}
    Edge<T> &operator[](int i)const{return l[i];}
  };
  struct const_tree_range{
    using iterator=typename vector<Edge<T>>::const_iterator;
    iterator l,r;
    iterator begin()const{return l;}
    iterator end()const{return r;}
    int size()const{return r-l;}
    bool empty()const{return !size();}
    const Edge<T> &operator[](int i)const{return l[i];}
  };
public:
  Tree(int n_):n(n_){
    edge.reserve(n-1);
  }
  Tree():n(0){}
  template<bool weighted=false,bool index=1>
  void read(){
    for(int i=0;i<n-1;i++){
      int u,v;
      T w=T();
      cin>>u>>v;
      if constexpr(index)u--,v--;
      if constexpr(weighted)cin>>w;
      else w=1;
      edge.emplace_back(u,v,w,i);
    }
    build();
  }
  template<bool index=1>
  void readp(){
    for(int i=1;i<n;i++){
      int p;
      cin>>p;
      if constexpr(index)p--;
      edge.emplace_back(i,p,1,i);
    }
    build();
  }
  void add_edge(int u,int v){edge.emplace_back(u,v,1,edge.size());}
  void add_edge(int u,int v,T w){edge.emplace_back(u,v,w,edge.size());}
  void add_edge(int u,int v,T w,int idx){edge.emplace_back(u,v,w,idx);}
  void build(){
    vector<int>cnt(n+1,0);
    for(auto&&[u,v,w,i]:edge)cnt[u+1]++,cnt[v+1]++;
    for(int i=1;i<=n;i++)cnt[i]+=cnt[i-1];
    assert(cnt[n]==n*2-2);
    ptr=cnt;
    g.resize(n*2-2);
    for(auto&&[u,v,w,i]:edge){
      g[cnt[u]++]=Edge(u,v,w,i);
      g[cnt[v]++]=Edge(v,u,w,i);
    }
  }
  void remove_parent(int root=0){
    edge.resize(n-1);
    auto dfs=[&](auto self,int x,int p,T w)->void {
      for(const Edge<T>&e:(*this)[x])if(e.to!=p){
        edge[e.index]=e;
        self(self,e.to,x,e.weight);
      }
    };
    dfs(dfs,root,-1,T());
    vector<int>cnt(n+1,0);
    for(int i=0;i<n-1;i++)cnt[edge[i].from+1]++;
    for(int i=1;i<=n;i++)cnt[i]+=cnt[i-1];
    ptr=cnt;
    g.resize(cnt[n]);
    for(const Edge<T>&e:edge)g[cnt[e.from]++]=e;
  }
  void hld(int root=0){
    remove_parent(root);
    auto dfs=[&](auto self,int x)->int {
      int ret=1,mx=-1;
      for(Edge<T>&e:(*this)[x]){
        int s=self(self,e.to);
        ret+=s;
        if(mx<s){
          mx=s;
          swap((*this)[x][0],e);
        }
      }
      return ret;
    };
    dfs(dfs,root);
  }
  int size()const{return n;}
  tree_range operator[](int i){return tree_range{g.begin()+ptr[i],g.begin()+ptr[i+1]};}
  const_tree_range operator[](int i)const{return const_tree_range{g.begin()+ptr[i],g.begin()+ptr[i+1]};}
  const Edge<T>& get_edge(int i)const{return edge[i];}
};
template<typename T>
vector<int>offline_lca(const Tree<T>&t,const vector<pair<int,int>>&query,int root=0){
  int n=t.size();
  UnionFind uf(n);
  vector<int>st(n),mark(n),p(n),ret(query.size(),-1);
  int top=0;
  st[0]=root;
  for(const auto&[l,r]:query)mark[l]++,mark[r]++;
  vector<int>ptr;
  vector<pair<int,int>>q(query.size()*2);
  {
    vector<int>cnt(n+1);
    cnt[0]=0;
    for(int i=1;i<=n;i++)cnt[i]=cnt[i-1]+mark[i-1];
    ptr=cnt;
    for(int i=0;i<query.size();i++){
      q[cnt[query[i].first]++]=make_pair(query[i].second,i);
      q[cnt[query[i].second]++]=make_pair(query[i].first,i);
    }
  }
  for(int i=0;i<n;i++){
    p[i]=t[i].size();
    mark[i]=-1;
  }
  auto run=[&](int u)->bool {
    while(p[u]){
      int v=t[u][--p[u]].to;
      if(mark[v]==-1){
        st[++top]=v;
        return true;
      }
    }
    return false;
  };
  while(top!=-1){
    int u=st[top];
    if(mark[u]==-1)mark[u]=u;
    else{
      uf.merge(u,t[u][p[u]].to);
      mark[uf.root(u)]=u;
    }
    if(!run(u)){
      for(int i=ptr[u];i<ptr[u+1];i++){
        const auto&[v,j]=q[i];
        if(mark[v]!=-1&&ret[j]==-1)ret[j]=mark[uf.root(v)];
      }
      top--;
    }
  }
  return ret;
}
using namespace std;
template<typename T,typename ADD,typename DEL,typename OUT>
void mo_tree(Tree<T>t,vector<pair<int,int>>query,const ADD&add,const DEL&del,const OUT&out){
  int n=t.size();
  int q=query.size();
  int b=n/min(n,(int)sqrt(q));
  vector<int>dfs_order(n),dfs_order2(n*2-1);
  int d=0;
  t.remove_parent();
  auto dfs=[&](auto self,int x)->void {
    dfs_order[x]=d;
    dfs_order2[d++]=x;
    for(const Edge<T>&e:t[x]){
      self(self,e.to);
      dfs_order2[d++]=e.to;
    }
  };
  dfs(dfs,0);
  debug(dfs_order,dfs_order2);
  vector<int>lca=offline_lca(t,query);
  for(auto&[u,v]:query){
    u=dfs_order[u]+1;
    v=dfs_order[v]+1;
    if(u>v)swap(u,v);
  }
  vector<int>ord(q);
  iota(ord.begin(),ord.end(),0);
  sort(ord.begin(),ord.end(),[&](int x,int y)->bool {
    const pair<int,int>&a=query[x],&c=query[y];
    if(a.first/b!=c.first/b)return a.first<c.first;
    return ((a.first/b)&1)?a.second>c.second:a.second<c.second;
  });
  int l=0,r=0;
  vector<char>contain(n,false);
  auto flip=[&](int id){
    contain[id]^=1;
    if(contain[id])add(id);
    else del(id);
  };
  for(int i:ord){
    while(l>query[i].first)flip(dfs_order2[--l]);
    while(r<query[i].second)flip(dfs_order2[r++]);
    while(l<query[i].first)flip(dfs_order2[l++]);
    while(r>query[i].second)flip(dfs_order2[--r]);
    flip(lca[i]);
    out(i);
    flip(lca[i]);
  }
}
template<typename T>
struct SquareRootDecomposition{
private:
  vector<T>block,dlock;
  int n,b;
public:
  SquareRootDecomposition(int n_):n(n_),b(sqrt(n_)){
    block.resize((n+b-1)/b,0);
    dlock.resize(n,0);
  }
  inline void add(int i,T x){
    dlock[i]+=x;
    block[i/b]+=x;
  }
  inline void set(int i,T x){
    add(i,x-dlock[i]);
  }
  T sum(int l,int r)const{
    T ret=0;
    if(r-l<b){
      reps(i,l,r)ret+=dlock[i];
    }
    else{
      int l2=(l+b-1)/b*b;
      int r2=r/b*b;
      reps(i,l,l2)ret+=dlock[i];
      reps(i,r2,r)ret+=dlock[i];
      l=l2/b,r=r2/b;
      reps(i,l,r)ret+=block[i];
    }
    return ret;
  }
  int lower_bound(int id,T k=1)const{
    assert(k);
    if(k>0){
      int id2=(id+b-1)/b*b;
      reps(i,id,id2){
        if((k-=dlock[i])<=0)return i;
      }
      id=id2/b;
      while(id<block.size()){
        if(k-block[id]<=0)break;
        k-=block[id++];
      }
      if(id==block.size())return n;
      id*=b;
      reps(i,id,id+b){
        if((k-=dlock[i])<=0)return i;
      }
    }
    else{
      k=-k;
      int id2=id/b*b;
      for(int i=id-1;i>=id2;i--){
        if((k-=dlock[i])<=0)return i+1;
      }
      id=id2/b;
      while(id){
        if(k-block[id-1]<=0)break;
        k-=block[--id];
      }
      if(id==0)return 0;
      id*=b;
      for(int i=id-1;i>=id-b;i--){
        if((k-=dlock[i])<=0)return i+1;
      }
    }
    return -1;
  }
  friend ostream &operator<<(ostream &os,const SquareRootDecomposition<T>&srd){
    os<<srd.dlock;
    return os;
  }
};
void SOLVE(){
  int n,q;
  cin>>n>>q;
  vector<int>v(n);
  cin>>v;
  auto z(v);
  sort(all(z)),z.erase(unique(all(z)),z.end());
  rep(i,n)v[i]=lower_bound(all(z),v[i])-z.begin();
  Tree t(n);
  t.read();
  vector<pair<int,int>>query(q);
  vector<int>k(q);
  vector<int>ans(q);
  rep(i,q){
    cin>>query[i]>>k[i];
    query[i]--;
  }
  SquareRootDecomposition<int>srd(z.size());
  mo_tree(t,query,[&](int x){srd.add(v[x],1);},[&](int x){srd.add(v[x],-1);},[&](int x){ans[x]=z[srd.lower_bound(0,k[x])];});
  rep(i,q)cout<<ans[i]<<'\n';
}
accuracy: 1
time_ms: 540
memory_kb: 25896
final_rank: 3

code_snippet: #include <bits/stdc++.h>
using namespace std;
// #if __has_include(<atcoder/all>)
// #include <atcoder/all>
// using namespace atcoder;
// template<int mod>istream &operator>>(istream &is,static_modint<mod> &a){long long b;is>>b;a=b;return is;}
// istream &operator>>(istream &is,modint &a){long long b;cin>>b;a=b;return is;}
// #endif
#ifdef LOCAL
#include "debug.h"
#else
#define debug(...) static_cast<void>(0)
#define debugg(...) static_cast<void>(0)
#define esper(...) static_cast<void>(0)
template<typename T1,typename T2>ostream &operator<<(ostream &os,const pair<T1,T2>&p){os<<p.first<<' '<<p.second;return os;}
#endif
using ll=long long;
using ull=unsigned long long;
using P=pair<ll,ll>;
template<typename T>using minque=priority_queue<T,vector<T>,greater<T>>;
template<typename T>bool chmax(T &a,const T &b){return (a<b?(a=b,true):false);}
template<typename T>bool chmin(T &a,const T &b){return (a>b?(a=b,true):false);}
template<typename T1,typename T2>istream &operator>>(istream &is,pair<T1,T2>&p){is>>p.first>>p.second;return is;}
template<typename T>istream &operator>>(istream &is,vector<T> &a){for(auto &i:a)is>>i;return is;}
template<typename T1,typename T2>void operator++(pair<T1,T2>&a,int n){a.first++,a.second++;}
template<typename T1,typename T2>void operator--(pair<T1,T2>&a,int n){a.first--,a.second--;}
template<typename T>void operator++(vector<T>&a,int n){for(auto &i:a)i++;}
template<typename T>void operator--(vector<T>&a,int n){for(auto &i:a)i--;}
#define reps(i,a,n) for(int i=(a);i<int(n);i++)
#define rep(i,n) reps(i,0,n)
#define all(x) x.begin(),x.end()
#define pcnt(x) __builtin_popcountll(x)
#define fin(x) return cout<<x<<'\n',static_cast<void>(0)
ll myceil(ll a,ll b){return (a+b-1)/b;}
template<typename T,size_t n,size_t id=0>
auto vec(const int (&d)[n],const T &init=T()){
  if constexpr (id<n)return vector(d[id],vec<T,n,id+1>(d,init));
  else return init;
}
void SOLVE();
int main(){
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cout<<fixed<<setprecision(16);
  #ifdef LOCAL
  clock_t start=clock();
  #endif
  int testcase=1;
  //cin>>testcase;
  for(int i=0;i<testcase;i++){
    SOLVE();
  }
  #ifdef LOCAL
  cerr<<"time:";
  cerr<<(clock()-start)/1000;
  cerr<<"ms\n";
  #endif
}
struct dsu{
  vector<int>par;
  dsu(int n):par(n,-1){}
  int leader(int u){
    while(par[u]>=0)u=par[u];
    return u;
  }
  void merge(int u,int v){
    int ru=leader(u),rv=leader(v);
    if(ru!=rv){
      if(par[ru]>par[rv])swap(ru,rv);
      par[ru]+=par[rv];
      par[rv]=ru;
    }
  }

};
vector<int>offline_lca(vector<vector<int>>&g,vector<pair<int,int>>query,int root=0){
  int n=g.size();
  dsu uf(n);
  vector<int>st(n),mark(n),p(n),ret(query.size(),-1);
  int top=0;
  st[0]=root;
  for(auto &&[l,r]:query)mark[l]++,mark[r]++;
  vector<vector<pair<int,int>>>q(n);
  rep(i,n){
    q[i].reserve(mark[i]);
    mark[i]=-1;
    p[i]=g[i].size();
  }
  rep(i,query.size()){
    q[query[i].first].emplace_back(query[i].second,i);
    q[query[i].second].emplace_back(query[i].first,i);
  }
  auto run=[&](int u)->bool {
    while(p[u]){
      int v=g[u][--p[u]];
      if(mark[v]==-1){
        st[++top]=v;
        return true;
      }
    }
    return false;
  };
  while(top!=-1){
    int u=st[top];
    if(mark[u]==-1)mark[u]=u;
    else{
      uf.merge(u,g[u][p[u]]);
      mark[uf.leader(u)]=u;
    }
    if(!run(u)){
      for(auto&&[v,i]:q[u]){
        if(mark[v]!=-1&&ret[i]==-1)ret[i]=mark[uf.leader(v)];
      }
      top--;
    }
  }
  return ret;
}
template<bool vertex_query=true>
struct MoTree{
public:
  vector<int>par;
private:
  vector<vector<int>>g;
  int n,st;
  vector<pair<int,int>>Q;
  vector<int>dfs_order;
  vector<int>dfs_order2;
  int d;
  void dfs(int x,int p){
    par[x]=p;
    dfs_order[x]=d;
    dfs_order2[d++]=x;
    for(int i:g[x])if(i!=p){
      dfs(i,x);
      dfs_order2[d++]=i;
    }
  }
public:
  MoTree(int n):n(n),g(n),dfs_order(n),dfs_order2(n*2-1),d(0),par(n){}
  void add_edge(int u,int v){
    g[u].push_back(v);
    g[v].push_back(u);
  }
  void add_query(int u,int v){
    Q.push_back({u,v});
  }
  template<typename F1,typename F2,typename F3,typename F4>
  void build(F1 init,F2 add,F3 del,F4 out){
    int q=Q.size();
    st=n/min<int>(n,sqrt(q));
    dfs(0,-1);
    vector<int>lca=offline_lca(g,Q);
    debug(lca);
    rep(i,Q.size()){
      Q[i].first=dfs_order[Q[i].first]+1;
      Q[i].second=dfs_order[Q[i].second]+1;
      if(Q[i].first>Q[i].second)swap(Q[i].first,Q[i].second);
    }
    vector<int>ord(Q.size());
    iota(all(ord),0);
    sort(all(ord),[&](int x,int y){
      pair<int,int>a=Q[x],b=Q[y];
      if(a.first/st!=b.first/st)return a.first<b.first;
      return ((a.first/st)&1)?a.second>b.second:a.second<b.second;
    });
    init();
    int l=0,r=0;
    vector<char>contain(n,false);
    auto f=[&](int id)->void {
      contain[id]^=1;
      if(contain[id])add(id);
      else del(id);
    };
    for(int i:ord){
      while(l>Q[i].first)f(dfs_order2[--l]);
      while(r<Q[i].second)f(dfs_order2[r++]);
      while(l<Q[i].first)f(dfs_order2[l++]);
      while(r>Q[i].second)f(dfs_order2[--r]);
      if constexpr(vertex_query)f(lca[i]);
      out(i);
      if constexpr(vertex_query)f(lca[i]);
    }
  }
};
void SOLVE(){
  int n,q;
  cin>>n>>q;
  vector<int>x(n);
  cin>>x;
  MoTree mo(n);
  rep(i,n-1){
    int u,v;
    cin>>u>>v;
    u--,v--;
    mo.add_edge(u,v);
  }
  vector<int>kth(q);
  rep(i,q){
    int u,v,k;
    cin>>u>>v>>k;
    u--,v--;
    mo.add_query(u,v);
    kth[i]=k;
  }
  int vsize=n*2-1;
  int b=sqrt(vsize)+1;
  vector<int>block((vsize+b-1)/b,0);
  vector<int>dlock(vsize,0);
  vector<int>z;
  vector<int>ans(q);
  auto init=[&]()->void {
    z=x;
    sort(all(z)),z.erase(unique(all(z)),z.end());
    rep(i,n)x[i]=lower_bound(all(z),x[i])-z.begin();
  };
  auto add=[&](int i)->void {
    dlock[x[i]]++;
    block[x[i]/b]++;
  };
  auto del=[&](int i)->void {
    dlock[x[i]]--;
    block[x[i]/b]--;
  };
  auto out=[&](int i)->void {
    int pos=0;
    while(kth[i]>block[pos])kth[i]-=block[pos++];
    pos*=b;
    while(kth[i]>dlock[pos])kth[i]-=dlock[pos++];
    ans[i]=z[pos];
  };
  mo.build(init,add,del,out);
  rep(i,q)cout<<ans[i]<<'\n';
}
accuracy: 1
time_ms: 570
memory_kb: 24432
final_rank: 2

code_snippet: #include <bits/stdc++.h>
using namespace std;

struct BIT{
  int N;
  vector<int> bit;
  BIT(int N_): N(N_), bit(N_ + 1, 0){
  }
  void add(int i, int x){
    i++; //0base->1base
    while (i <= N){
      bit[i] += x;
      i += i & -i;
    }
  }
  int sum(int i){ //1base
    int ans = 0;
    while (i > 0){
      ans += bit[i];
      i -= i & -i;
    }
    return ans;
  }
  int sum(int L, int R){
    return sum(R) - sum(L);
  }
};

int main() {
  int n,q;
  cin >> n >> q;
  
  vector<int> x(n);
  for(auto &xa:x) cin >> xa;
  vector<vector<int>> g(n);
  int a,b;
  for(int i=0;i<n-1;i++){
    cin >> a >> b;
    a--;b--;
    g[a].push_back(b);
    g[b].push_back(a);
  }
  vector<int> s1(q),s2(q),l(q),r(q);
  for(int i=0;i<q;i++){ 
    cin >> s1[i] >> s2[i] >> l[i];
    s1[i]--;s2[i]--;
  }
  
  vector<int> xs=x;
  sort(xs.begin(),xs.end());
  xs.erase(unique(xs.begin(),xs.end()),xs.end());
  int cnt=(int)xs.size();
  vector<vector<int>> add(cnt);
  for(int i=0;i<n;i++){
    x[i]=find(xs.begin(),xs.end(),x[i])-xs.begin();
    add[x[i]].push_back(i);
  }

  int k=17;

  vector<int> in(n),out(n),dp(n);
  vector pr(n,vector<int>(k+2,-1));
  int t=0;
  function<void(int,int,int)> dfs=[&](int p,int cl,int d){
    in[cl]=t;
    t++;
    pr[cl][0]=p;
    dp[cl]=d;
    for(auto nx:g[cl]){
      if(nx==p) continue;
      dfs(cl,nx,d+1);
    }
    out[cl]=t;
    t++;
  };
  dfs(-1,0,0);

  for (int i = 0; i <=k; i++){
    for (int j = 0; j < n; j++){
      if (pr[j][i] != -1){
        pr[j][i+1] = pr[pr[j][i]][i];
      }
    }
  }

  function<int(int,int)> lca=[&](int u, int v){
    //cout << u << " " << v << endl;
    if (dp[u] > dp[v]) swap(u, v);
    int dif=dp[v]-dp[u];
    for(int i=0;i<=k;i++){
      if((dif>>i&1)==1) v=pr[v][i];
     }
      //cout << v << "V" << dp[v] << " " << dp[u] <<endl;
   
    if (u == v) return u;

    for (int i = k; i >= 0; i--){
      if (pr[u][i] != pr[v][i]){
        u = pr[u][i];
        v = pr[v][i];
      }
    }
    return pr[u][0];
  };
 
  for(int i=0;i<q;i++){ 
    r[i]=lca(s1[i],s2[i]);
    //cout << i << " " << r[i] << endl;
  }

  vector<int> tv(q, 0), fv(q, cnt);
  while (true){
    bool ok = true;
    vector<vector<int>> query(cnt);
    for (int i = 0; i < q; i++){
      if (fv[i] - tv[i] > 1){
        ok = false;
        query[(tv[i] + fv[i]) / 2].push_back(i);
      }
    }
    if (ok){
      break;
    }
    BIT bit(n * 2);
    for (int i = 0; i < cnt; i++){
      for (int j : query[i]){
         if(fv[j]-tv[j]<=1) continue; 
        int sum = bit.sum(in[r[j]] + 1, in[s1[j]] + 1) + bit.sum(in[r[j]] + 1, in[s2[j]] + 1);
        if (x[r[j]] < i){
          sum++;
        }
        //cout << i << " " << j << " " <<sum << " " << l[j] << endl;
        if (sum < l[j]){
          tv[j] = i;
        } else {
          fv[j] = i;
        }
      }
      for (int j : add[i]){
        bit.add(in[j], 1);
        bit.add(out[j], -1);
      }
      //for(int k=0;k<N*2;k++) cout << BIT.sum(k,k+1) << " ";
      //cout << endl;
    }
  }
 
  //cout << cnt << endl;
  
  for (int i = 0; i < q; i++){
    cout << xs[tv[i]] << endl;
  }

}
accuracy: 1
time_ms: 1530
memory_kb: 39752
final_rank: 6

code_snippet: #include <bits/stdc++.h>
using namespace std;

struct BIT{
  int N;
  vector<int> bit;
  BIT(int N_): N(N_), bit(N_ + 1, 0){
  }
  void add(int i, int x){
    i++; //0base->1base
    while (i <= N){
      bit[i] += x;
      i += i & -i;
    }
  }
  int sum(int i){ //1base
    int ans = 0;
    while (i > 0){
      ans += bit[i];
      i -= i & -i;
    }
    return ans;
  }
  int sum(int L, int R){
    return sum(R) - sum(L);
  }
};

int main() {
  int n,q;
  cin >> n >> q;
  
  vector<int> x(n);
  for(auto &xa:x) cin >> xa;
  vector<vector<int>> g(n);
  int a,b;
  for(int i=0;i<n-1;i++){
    cin >> a >> b;
    a--;b--;
    g[a].push_back(b);
    g[b].push_back(a);
  }
  vector<int> s1(q),s2(q),l(q),r(q);
  for(int i=0;i<q;i++){ 
    cin >> s1[i] >> s2[i] >> l[i];
    s1[i]--;s2[i]--;
  }
  
  vector<int> xs=x;
  sort(xs.begin(),xs.end());
  xs.erase(unique(xs.begin(),xs.end()),xs.end());
  int cnt=(int)xs.size();
  vector<vector<int>> add(cnt);
  for(int i=0;i<n;i++){
    x[i]=find(xs.begin(),xs.end(),x[i])-xs.begin();
    add[x[i]].push_back(i);
  }

  int k=17;

  vector<int> in(n),out(n),dp(n);
  vector pr(n,vector<int>(k+2,-1));
  int t=0;
  function<void(int,int,int)> dfs=[&](int p,int cl,int d){
    in[cl]=t;
    t++;
    pr[cl][0]=p;
    dp[cl]=d;
    for(auto nx:g[cl]){
      if(nx==p) continue;
      dfs(cl,nx,d+1);
    }
    out[cl]=t;
    t++;
  };
  dfs(-1,0,0);

  for (int i = 0; i <=k; i++){
    for (int j = 0; j < n; j++){
      if (pr[j][i] != -1){
        pr[j][i+1] = pr[pr[j][i]][i];
      }
    }
  }

  function<int(int,int)> lca=[&](int u, int v){
    //cout << u << " " << v << endl;
    if (dp[u] > dp[v]) swap(u, v);
    int dif=dp[v]-dp[u];
    for(int i=0;i<=k;i++){
      if((dif>>i&1)==1) v=pr[v][i];
     }
      //cout << v << "V" << dp[v] << " " << dp[u] <<endl;
   
    if (u == v) return u;

    for (int i = k; i >= 0; i--){
      if (pr[u][i] != pr[v][i]){
        u = pr[u][i];
        v = pr[v][i];
      }
    }
    return pr[u][0];
  };
 
  for(int i=0;i<q;i++){ 
    r[i]=lca(s1[i],s2[i]);
    //cout << i << " " << r[i] << endl;
  }

  vector<int> tv(q, 0), fv(q, cnt);
  while (true){
    bool ok = true;
    vector<vector<int>> query(cnt);
    for (int i = 0; i < q; i++){
      if (fv[i] - tv[i] > 1){
        ok = false;
        query[(tv[i] + fv[i]) / 2].push_back(i);
      }
    }
    if (ok){
      break;
    }
    BIT bit(n * 2);
    for (int i = 0; i < cnt; i++){
      for (int j : query[i]){
        int sum = bit.sum(in[r[j]] + 1, in[s1[j]] + 1) + bit.sum(in[r[j]] + 1, in[s2[j]] + 1);
        if (x[r[j]] < i){
          sum++;
        }
        //cout << i << " " << j << " " <<sum << " " << l[j] << endl;
        if (sum < l[j]){
          tv[j] = i;
        } else {
          fv[j] = i;
        }
      }
      for (int j : add[i]){
        bit.add(in[j], 1);
        bit.add(out[j], -1);
      }
      //for(int k=0;k<N*2;k++) cout << BIT.sum(k,k+1) << " ";
      //cout << endl;
    }
  }
 
  //cout << cnt << endl;
  
  for (int i = 0; i < q; i++){
    cout << xs[tv[i]] << endl;
  }

}
accuracy: 1
time_ms: 1530
memory_kb: 39832
final_rank: 7

code_snippet: #include <bits/stdc++.h>
using namespace std;

struct BIT{
  int N;
  vector<int> bit;
  BIT(int N_): N(N_), bit(N_ + 1, 0){
  }
  void add(int i, int x){
    i++; //0base->1base
    while (i <= N){
      bit[i] += x;
      i += i & -i;
    }
  }
  int sum(int i){ //1base
    int ans = 0;
    while (i > 0){
      ans += bit[i];
      i -= i & -i;
    }
    return ans;
  }
  int sum(int L, int R){
    return sum(R) - sum(L);
  }
};

int main() {
  int n,q;
  cin >> n >> q;
  
  vector<int> x(n);
  for(auto &xa:x) cin >> xa;
  vector<vector<int>> g(n);
  int a,b;
  for(int i=0;i<n-1;i++){
    cin >> a >> b;
    a--;b--;
    g[a].push_back(b);
    g[b].push_back(a);
  }
  vector<int> s1(q),s2(q),l(q),r(q);
  for(int i=0;i<q;i++){ 
    cin >> s1[i] >> s2[i] >> l[i];
    s1[i]--;s2[i]--;
  }
  
  vector<int> xs=x;
  sort(xs.begin(),xs.end());
  xs.erase(unique(xs.begin(),xs.end()),xs.end());
  int cnt=(int)xs.size();
  vector<vector<int>> add(cnt);
  for(int i=0;i<n;i++){
    x[i]=find(xs.begin(),xs.end(),x[i])-xs.begin();
    add[x[i]].push_back(i);
  }

  int k=2,kk=2;
  while(kk<n){k++; kk=kk*kk;}
  //cout << k << endl;

  vector<int> in(n),out(n),dp(n);
  vector pr(n,vector<int>(k+2,-1));
  int t=0;
  function<void(int,int,int)> dfs=[&](int p,int cl,int d){
    in[cl]=t;
    t++;
    pr[cl][0]=p;
    dp[cl]=d;
    for(auto nx:g[cl]){
      if(nx==p) continue;
      dfs(cl,nx,d+1);
    }
    out[cl]=t;
    t++;
  };
  dfs(-1,0,0);

  for (int i = 0; i <=k; i++){
    for (int j = 0; j < n; j++){
      if (pr[j][i] != -1){
        pr[j][i+1] = pr[pr[j][i]][i];
      }
    }
  }

  function<int(int,int)> lca=[&](int u, int v){
    //cout << u << " " << v << endl;
    if (dp[u] > dp[v]) swap(u, v);
    int dif=dp[v]-dp[u];
    for(int i=0;i<=k;i++){
      if((dif>>i&1)==1) v=pr[v][i];
     }
      //cout << v << "V" << dp[v] << " " << dp[u] <<endl;
   
    if (u == v) return u;

    for (int i = k; i >= 0; i--){
      if (pr[u][i] != pr[v][i]){
        u = pr[u][i];
        v = pr[v][i];
      }
    }
    return pr[u][0];
  };
 
  for(int i=0;i<q;i++){ 
    r[i]=lca(s1[i],s2[i]);
    //cout << i << " " << r[i] << endl;
  }

  vector<int> tv(q,-1),ss(q,0);
  BIT bit(n*2);
  for (int i = 0; i < cnt; i++){
    for (int j : add[i]){
      bit.add(in[j], 1);
      bit.add(out[j], -1);
    }
    //for(int k1=0;k1<n*2;k1++) cout << bit.sum(k1,k1+1) << " ";
    //cout << endl;
    
    for(int j=0;j<q;j++){
      if(tv[j]!=-1) continue;
      int sum = bit.sum(in[r[j]] + 1, in[s1[j]] + 1) + bit.sum(in[r[j]] + 1, in[s2[j]] + 1);
      if (x[r[j]] <= i) sum++;
        //cout << i << " " << j << " " <<sum << " " << l[j] << endl;
      if((sum-ss[j]>1)&&(ss[j]+1<=l[j])&&(sum>=l[j])) tv[j]=i;
      else if(sum==l[j]) tv[j]=i;
      else ss[j]=sum;
    }
  }
 
  //cout << cnt << endl;
  
  for (int i = 0; i < q; i++){
    cout << xs[tv[i]] << endl;
  }

}
accuracy: 0.41935483870967744
time_ms: 10
memory_kb: 3456
final_rank: 20

code_snippet: #include <bits/stdc++.h>
using namespace std;
template <class Key> struct RandomizedBinarySearchTree {
  inline int xor128() {
    static int x = 123456789;
    static int y = 362436069;
    static int z = 521288629;
    static int w = 88675123;
    int t;
    t = x ^ (x << 11);
    x = y;
    y = z;
    z = w;
    return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
  }
  struct Node {
    Node *l, *r;
    int cnt;
    Key key;
    Node() {}
    Node(const Key &k) : cnt(1), key(k), l(nullptr), r(nullptr) {}
  };
  vector<Node> pool;
  int ptr;
  RandomizedBinarySearchTree(int sz) : pool(sz), ptr(0) {}
  inline Node *alloc(const Key &key) { return &(pool[ptr++] = Node(key)); }
  virtual Node *clone(Node *t) { return t; }
  inline int count(const Node *t) { return t ? t->cnt : 0; }
  inline Node *update(Node *t) {
    t->cnt = count(t->l) + count(t->r) + 1;
    return t;
  }
  Node *propagete(Node *t) { return update(clone(t)); }
  Node *merge(Node *l, Node *r) {
    if (!l || !r)
      return l ? l : r;
    if (xor128() % (l->cnt + r->cnt) < l->cnt) {
      l = propagete(l);
      l->r = merge(l->r, r);
      return update(l);
    } else {
      r = propagete(r);
      r->l = merge(l, r->l);
      return update(r);
    }
  }
  pair<Node *, Node *> split(Node *t, int k) {
    if (!t)
      return {t, t};
    t = propagete(t);
    if (k <= count(t->l)) {
      auto s = split(t->l, k);
      t->l = s.second;
      return {s.first, update(t)};
    } else {
      auto s = split(t->r, k - count(t->l) - 1);
      t->r = s.first;
      return {update(t), s.second};
    }
  }
  Node *build(int l, int r, const vector<Key> &v) {
    if (l + 1 >= r)
      return alloc(v[l]);
    return merge(build(l, (l + r) >> 1, v), build((l + r) >> 1, r, v));
  }
  Node *build(const vector<Key> &v) {
    ptr = 0;
    return build(0, (int)v.size(), v);
  }
  void dump(Node *r, typename vector<Key>::iterator &it) {
    if (!r)
      return;
    r = propagete(r);
    dump(r->l, it);
    *it = r->key;
    dump(r->r, ++it);
  }
  vector<Key> dump(Node *r) {
    vector<Key> v((size_t)count(r));
    auto it = begin(v);
    dump(r, it);
    return v;
  }
  string to_string(Node *r) {
    auto s = dump(r);
    string ret;
    for (int i = 0; i < s.size(); i++)
      ret += ", ";
    return (ret);
  }
  void insert(Node *&t, int k, const Key &v) {
    auto x = split(t, k);
    t = merge(merge(x.first, alloc(v)), x.second);
  }
  void erase(Node *&t, int k) {
    auto x = split(t, k);
    t = merge(x.first, split(x.second, 1).second);
  }
  int size(Node *t) { return count(t); }
  bool empty(Node *t) { return !t; }
  Node *makeset() { return (nullptr); }
};
template <class Key>
struct PresidentRandomizedBinarySearchTree : RandomizedBinarySearchTree<Key> {
  using RBST = RandomizedBinarySearchTree<Key>;
  using Node = typename RBST::Node;
  PresidentRandomizedBinarySearchTree(int sz) : RBST(sz) {}
  Node *clone(Node *t) override { return &(RBST::pool[RBST::ptr++] = *t); }
  Node *rebuild(Node *r) { return RBST::build(RBST::dump(r)); }
};
template <class T>
struct OrderedMultiSet : PresidentRandomizedBinarySearchTree<T> {
  using RBST = PresidentRandomizedBinarySearchTree<T>;
  using Node = typename RBST::Node;
  OrderedMultiSet(int sz) : RBST(sz) {}
  T kth_element(Node *t, int k) {
    if (k < RBST::count(t->l))
      return kth_element(t->l, k);
    if (k == RBST::count(t->l))
      return t->key;
    return kth_element(t->r, k - RBST::count(t->l) - 1);
  }
  virtual void insert_key(Node *&t, const T &x) {
    RBST::insert(t, lower_bound(t, x), x);
  }
  void erase_key(Node *&t, const T &x) {
    if (!count(t, x))
      return;
    RBST::erase(t, lower_bound(t, x));
  }
  int count(Node *t, const T &x) {
    return upper_bound(t, x) - lower_bound(t, x);
  }
  int lower_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x <= t->key)
      return lower_bound(t->l, x);
    return lower_bound(t->r, x) + RBST::count(t->l) + 1;
  }
  int upper_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x < t->key)
      return upper_bound(t->l, x);
    return upper_bound(t->r, x) + RBST::count(t->l) + 1;
  }
};
struct CentroidPathDecomposition {
  struct Centroid {
    int ParIndex, ParDepth, Deep;
    vector<int> node;
    Centroid(int idx, int dep, int deep)
        : ParIndex(idx), ParDepth(dep), Deep(deep) {}
    inline size_t size() { return (node.size()); }
    inline int &operator[](int k) { return (node[k]); }
    inline pair<int, int> Up() { return (make_pair(ParIndex, ParDepth)); }
  };
  vector<vector<int>> graph;
  vector<int> SubTreeSize, NextPath;
  vector<int> TreeIndex, TreeDepth;
  vector<Centroid> Centroids;
  void BuildSubTreeSize() {
    stack<pair<int, int>> s;
    s.emplace(0, -1);
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      if (~SubTreeSize[p.first]) {
        NextPath[p.first] = -1;
        for (auto &to : graph[p.first]) {
          if (p.second == to)
            continue;
          SubTreeSize[p.first] += SubTreeSize[to];
          if (NextPath[p.first] == -1 ||
              SubTreeSize[NextPath[p.first]] < SubTreeSize[to]) {
            NextPath[p.first] = to;
          }
        }
      } else {
        s.push(p);
        SubTreeSize[p.first] = 1;
        for (auto &to : graph[p.first]) {
          if (p.second != to)
            s.emplace(to, p.first);
        }
      }
    }
  }
  void BuildPath() {
    stack<pair<int, int>> s;
    Centroids.emplace_back(-1, -1, 0);
    s.emplace(0, -1);
    TreeIndex[0] = 0;
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      TreeDepth[p.first] = (int)Centroids[TreeIndex[p.first]].size();
      for (auto &to : graph[p.first]) {
        if (p.second == to)
          continue;
        if (to == NextPath[p.first]) {
          TreeIndex[to] = TreeIndex[p.first];
        } else {
          TreeIndex[to] = (int)Centroids.size();
          Centroids.emplace_back(TreeIndex[p.first], TreeDepth[p.first],
                                 Centroids[TreeIndex[p.first]].Deep + 1);
        }
        s.emplace(to, p.first);
      }
      Centroids[TreeIndex[p.first]].node.emplace_back(p.first);
    }
  }
  void AddEdge(int x, int y) {
    graph[x].push_back(y);
    graph[y].push_back(x);
  }
  virtual void Build() {
    BuildSubTreeSize();
    BuildPath();
  }
  inline size_t size() { return (Centroids.size()); }
  inline pair<int, int> Information(int idx) {
    return (make_pair(TreeIndex[idx], TreeDepth[idx]));
  }
  inline Centroid &operator[](int k) { return (Centroids[k]); }
  inline int LCA(int a, int b) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    return (Centroids[TreeIdxA][TreeDepthA]);
  }
  inline virtual void query(int a, int b,
                            const function<void(int, int, int)> &f) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        f(TreeIdxA, 0, TreeDepthA + 1);
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        f(TreeIdxB, 0, TreeDepthB + 1);
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    f(TreeIdxA, TreeDepthA, TreeDepthB + 1);
  }
  CentroidPathDecomposition(int SZ) {
    graph.resize(SZ);
    SubTreeSize.assign(SZ, -1);
    NextPath.resize(SZ);
    TreeIndex.resize(SZ);
    TreeDepth.resize(SZ);
  }
};
OrderedMultiSet<int> tree(5000000);
OrderedMultiSet<int>::Node *nodes[100000];
int N, Q, X[100000];
vector<int> g[100000];
int parent[100000];
void dfs(int idx, int par, OrderedMultiSet<int>::Node *par_set) {
  parent[idx] = par;
  tree.insert_key(par_set, X[idx]);
  nodes[idx] = par_set;
  for (auto &to : g[idx])
    if (to != par)
      dfs(to, idx, par_set);
}
int main() {
  scanf("%d %d", &N, &Q);
  CentroidPathDecomposition gg(N);
  vector<int> vs;
  for (int i = 0; i < N; i++) {
    scanf("%d", &X[i]);
    vs.emplace_back(X[i]);
  }
  sort(begin(vs), end(vs));
  vs.erase(unique(begin(vs), end(vs)), end(vs));
  for (int i = 1; i < N; i++) {
    int x, y;
    scanf("%d %d", &x, &y);
    --x, --y;
    g[x].emplace_back(y);
    g[y].emplace_back(x);
    gg.AddEdge(x, y);
  }
  dfs(0, -1, tree.makeset());
  gg.Build();
  for (int i = 0; i < Q; i++) {
    int x, y, z;
    scanf("%d %d %d", &x, &y, &z);
    --x, --y;
    int lca = gg.LCA(x, y);
    int ng = -1, ok = (int)vs.size() - 1;
    auto sum = [&](int v) {
      int ret = 0;
      ret += tree.upper_bound(nodes[x], vs[v]);
      ret += tree.upper_bound(nodes[y], vs[v]);
      ret -= X[lca] <= vs[v];
      if (lca)
        ret -= 2 * tree.upper_bound(nodes[parent[lca]], vs[v]);
      return ret;
    };
    while (ok - ng > 1) {
      int mid = (ok + ng) >> 1;
      if (sum(mid) >= z)
        ok = mid;
      else
        ng = mid;
    }
    printf("%d\n", vs[ok]);
  }
}
accuracy: 1
time_ms: 780
memory_kb: 132436
final_rank: 10

code_snippet: #include <bits/stdc++.h>
using namespace std;
template <class Key> struct RandomizedBinarySearchTree {
  inline int xor128() {
    static int x = 123456789;
    static int y = 362436069;
    static int z = 521288629;
    static int w = 88675123;
    int t;
    t = x ^ (x << 11);
    x = y;
    y = z;
    z = w;
    return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
  }
  struct Node {
    Node *l, *r;
    int cnt;
    Key key;
    Node() {}
    Node(const Key &k) : cnt(1), key(k), l(nullptr), r(nullptr) {}
  };
  vector<Node> pool;
  int ptr;
  RandomizedBinarySearchTree(int sz) : pool(sz), ptr(0) {}
  inline Node *alloc(const Key &key) { return &(pool[ptr++] = Node(key)); }
  virtual Node *clone(Node *t) { return t; }
  inline int count(const Node *t) { return t ? t->cnt : 0; }
  inline Node *update(Node *t) {
    t->cnt = count(t->l) + count(t->r) + 1;
    return t;
  }
  Node *propagete(Node *t) { return update(clone(t)); }
  Node *merge(Node *l, Node *r) {
    if (!l || !r)
      return l ? l : r;
    if (xor128() % (l->cnt + r->cnt) < l->cnt) {
      l = propagete(l);
      l->r = merge(l->r, r);
      return update(l);
    } else {
      r = propagete(r);
      r->l = merge(l, r->l);
      return update(r);
    }
  }
  pair<Node *, Node *> split(Node *t, int k) {
    if (!t)
      return {t, t};
    t = propagete(t);
    if (k <= count(t->l)) {
      auto s = split(t->l, k);
      t->l = s.second;
      return {s.first, update(t)};
    } else {
      auto s = split(t->r, k - count(t->l) - 1);
      t->r = s.first;
      return {update(t), s.second};
    }
  }
  Node *build(int l, int r, const vector<Key> &v) {
    if (l + 1 >= r)
      return alloc(v[l]);
    return merge(build(l, (l + r) >> 1, v), build((l + r) >> 1, r, v));
  }
  Node *build(const vector<Key> &v) {
    ptr = 0;
    return build(0, (int)v.size(), v);
  }
  void dump(Node *r, typename vector<Key>::iterator &it) {
    if (!r)
      return;
    r = propagete(r);
    dump(r->l, it);
    *it = r->key;
    dump(r->r, ++it);
  }
  vector<Key> dump(Node *r) {
    vector<Key> v((size_t)count(r));
    auto it = begin(v);
    dump(r, it);
    return v;
  }
  string to_string(Node *r) {
    auto s = dump(r);
    string ret;
    for (int i = 0; i < s.size(); i++)
      ret += ", ";
    return (ret);
  }
  void insert(Node *&t, int k, const Key &v) {
    auto x = split(t, k);
    t = merge(merge(x.first, alloc(v)), x.second);
  }
  void erase(Node *&t, int k) {
    auto x = split(t, k);
    t = merge(x.first, split(x.second, 1).second);
  }
  int size(Node *t) { return count(t); }
  bool empty(Node *t) { return !t; }
  Node *makeset() { return (nullptr); }
};
template <class Key>
struct PresidentRandomizedBinarySearchTree : RandomizedBinarySearchTree<Key> {
  using RBST = RandomizedBinarySearchTree<Key>;
  using Node = typename RBST::Node;
  PresidentRandomizedBinarySearchTree(int sz) : RBST(sz) {}
  Node *clone(Node *t) override { return &(RBST::pool[RBST::ptr++] = *t); }
  Node *rebuild(Node *r) { return RBST::build(RBST::dump(r)); }
};
template <class T>
struct OrderedMultiSet : PresidentRandomizedBinarySearchTree<T> {
  using RBST = PresidentRandomizedBinarySearchTree<T>;
  using Node = typename RBST::Node;
  OrderedMultiSet(int sz) : RBST(sz) {}
  T kth_element(Node *t, int k) {
    if (k < RBST::count(t->l))
      return kth_element(t->l, k);
    if (k == RBST::count(t->l))
      return t->key;
    return kth_element(t->r, k - RBST::count(t->l) - 1);
  }
  virtual void insert_key(Node *&t, const T &x) {
    RBST::insert(t, lower_bound(t, x), x);
  }
  void erase_key(Node *&t, const T &x) {
    if (!count(t, x))
      return;
    RBST::erase(t, lower_bound(t, x));
  }
  int count(Node *t, const T &x) {
    return upper_bound(t, x) - lower_bound(t, x);
  }
  int lower_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x <= t->key)
      return lower_bound(t->l, x);
    return lower_bound(t->r, x) + RBST::count(t->l) + 1;
  }
  int upper_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x < t->key)
      return upper_bound(t->l, x);
    return upper_bound(t->r, x) + RBST::count(t->l) + 1;
  }
};
struct CentroidPathDecomposition {
  struct Centroid {
    int ParIndex, ParDepth, Deep;
    vector<int> node;
    Centroid(int idx, int dep, int deep)
        : ParIndex(idx), ParDepth(dep), Deep(deep) {}
    inline size_t size() { return (node.size()); }
    inline int &operator[](int k) { return (node[k]); }
    inline pair<int, int> Up() { return (make_pair(ParIndex, ParDepth)); }
  };
  vector<vector<int>> graph;
  vector<int> SubTreeSize, NextPath;
  vector<int> TreeIndex, TreeDepth;
  vector<Centroid> Centroids;
  void BuildSubTreeSize() {
    stack<pair<int, int>> s;
    s.emplace(0, -1);
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      if (~SubTreeSize[p.first]) {
        NextPath[p.first] = -1;
        for (auto &to : graph[p.first]) {
          if (p.second == to)
            continue;
          SubTreeSize[p.first] += SubTreeSize[to];
          if (NextPath[p.first] == -1 ||
              SubTreeSize[NextPath[p.first]] < SubTreeSize[to]) {
            NextPath[p.first] = to;
          }
        }
      } else {
        s.push(p);
        SubTreeSize[p.first] = 1;
        for (auto &to : graph[p.first]) {
          if (p.second != to)
            s.emplace(to, p.first);
        }
      }
    }
  }
  void BuildPath() {
    stack<pair<int, int>> s;
    Centroids.emplace_back(-1, -1, 0);
    s.emplace(0, -1);
    TreeIndex[0] = 0;
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      TreeDepth[p.first] = (int)Centroids[TreeIndex[p.first]].size();
      for (auto &to : graph[p.first]) {
        if (p.second == to)
          continue;
        if (to == NextPath[p.first]) {
          TreeIndex[to] = TreeIndex[p.first];
        } else {
          TreeIndex[to] = (int)Centroids.size();
          Centroids.emplace_back(TreeIndex[p.first], TreeDepth[p.first],
                                 Centroids[TreeIndex[p.first]].Deep + 1);
        }
        s.emplace(to, p.first);
      }
      Centroids[TreeIndex[p.first]].node.emplace_back(p.first);
    }
  }
  void AddEdge(int x, int y) {
    graph[x].push_back(y);
    graph[y].push_back(x);
  }
  virtual void Build() {
    BuildSubTreeSize();
    BuildPath();
  }
  inline size_t size() { return (Centroids.size()); }
  inline pair<int, int> Information(int idx) {
    return (make_pair(TreeIndex[idx], TreeDepth[idx]));
  }
  inline Centroid &operator[](int k) { return (Centroids[k]); }
  inline int LCA(int a, int b) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    return (Centroids[TreeIdxA][TreeDepthA]);
  }
  inline virtual void query(int a, int b,
                            const function<void(int, int, int)> &f) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        f(TreeIdxA, 0, TreeDepthA + 1);
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        f(TreeIdxB, 0, TreeDepthB + 1);
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    f(TreeIdxA, TreeDepthA, TreeDepthB + 1);
  }
  CentroidPathDecomposition(int SZ) {
    graph.resize(SZ);
    SubTreeSize.assign(SZ, -1);
    NextPath.resize(SZ);
    TreeIndex.resize(SZ);
    TreeDepth.resize(SZ);
  }
};
OrderedMultiSet<int> tree(5000000);
OrderedMultiSet<int>::Node *nodes[100000];
int N, Q, X[100000];
vector<int> g[100000];
int parent[100000];
void dfs(int idx, int par, OrderedMultiSet<int>::Node *par_set) {
  parent[idx] = par;
  tree.insert_key(par_set, X[idx]);
  nodes[idx] = par_set;
  for (auto &to : g[idx])
    if (to != par)
      dfs(to, idx, par_set);
}
int main() {
  scanf("%d %d", &N, &Q);
  CentroidPathDecomposition gg(N);
  vector<int> vs;
  for (int i = 0; i < N; i++) {
    scanf("%d", &X[i]);
    vs.emplace_back(X[i]);
  }
  sort(begin(vs), end(vs));
  vs.erase(unique(begin(vs), end(vs)), end(vs));
  for (int i = 1; i < N; i++) {
    int x, y;
    scanf("%d %d", &x, &y);
    --x, --y;
    g[x].emplace_back(y);
    g[y].emplace_back(x);
    gg.AddEdge(x, y);
  }
  dfs(0, -1, tree.makeset());
  gg.Build();
  for (int i = 0; i < Q; i++) {
    int x, y, z;
    scanf("%d %d %d", &x, &y, &z);
    --x, --y;
    int lca = gg.LCA(x, y);
    int ng = -1, ok = (int)vs.size() - 1;
    auto sum = [&](int v) {
      int ret = 0;
      ret += tree.upper_bound(nodes[x], vs[v]);
      ret += tree.upper_bound(nodes[y], vs[v]);
      ret -= tree.upper_bound(nodes[lca], vs[v]);
      if (lca)
        ret -= tree.upper_bound(nodes[parent[lca]], vs[v]);
      return ret;
    };
    while (ok - ng > 1) {
      int mid = (ok + ng) >> 1;
      if (sum(mid) >= z)
        ok = mid;
      else
        ng = mid;
    }
    printf("%d\n", vs[ok]);
  }
}
accuracy: 1
time_ms: 850
memory_kb: 133116
final_rank: 11

code_snippet: #include <bits/stdc++.h>
using namespace std;
template <class Key> struct RandomizedBinarySearchTree {
  inline int xor128() {
    static int x = 123456789;
    static int y = 362436069;
    static int z = 521288629;
    static int w = 88675123;
    int t;
    t = x ^ (x << 11);
    x = y;
    y = z;
    z = w;
    return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
  }
  struct Node {
    Node *l, *r;
    int cnt;
    Key key;
    Node() {}
    Node(const Key &k) : cnt(1), key(k), l(nullptr), r(nullptr) {}
  };
  vector<Node> pool;
  int ptr;
  RandomizedBinarySearchTree(int sz) : pool(sz), ptr(0) {}
  inline Node *alloc(const Key &key) { return &(pool[ptr++] = Node(key)); }
  virtual Node *clone(Node *t) { return t; }
  inline int count(const Node *t) { return t ? t->cnt : 0; }
  inline Node *update(Node *t) {
    t->cnt = count(t->l) + count(t->r) + 1;
    return t;
  }
  Node *propagete(Node *t) { return update(clone(t)); }
  Node *merge(Node *l, Node *r) {
    if (!l || !r)
      return l ? l : r;
    if (xor128() % (l->cnt + r->cnt) > l->cnt) {
      l = propagete(l);
      l->r = merge(l->r, r);
      return update(l);
    } else {
      r = propagete(r);
      r->l = merge(l, r->l);
      return update(r);
    }
  }
  pair<Node *, Node *> split(Node *t, int k) {
    if (!t)
      return {t, t};
    t = propagete(t);
    if (k <= count(t->l)) {
      auto s = split(t->l, k);
      t->l = s.second;
      return {s.first, update(t)};
    } else {
      auto s = split(t->r, k - count(t->l) - 1);
      t->r = s.first;
      return {update(t), s.second};
    }
  }
  Node *build(int l, int r, const vector<Key> &v) {
    if (l + 1 >= r)
      return alloc(v[l]);
    return merge(build(l, (l + r) >> 1, v), build((l + r) >> 1, r, v));
  }
  Node *build(const vector<Key> &v) {
    ptr = 0;
    return build(0, (int)v.size(), v);
  }
  void dump(Node *r, typename vector<Key>::iterator &it) {
    if (!r)
      return;
    r = propagete(r);
    dump(r->l, it);
    *it = r->key;
    dump(r->r, ++it);
  }
  vector<Key> dump(Node *r) {
    vector<Key> v((size_t)count(r));
    auto it = begin(v);
    dump(r, it);
    return v;
  }
  string to_string(Node *r) {
    auto s = dump(r);
    string ret;
    for (int i = 0; i < s.size(); i++)
      ret += ", ";
    return (ret);
  }
  void insert(Node *&t, int k, const Key &v) {
    auto x = split(t, k);
    t = merge(merge(x.first, alloc(v)), x.second);
  }
  void erase(Node *&t, int k) {
    auto x = split(t, k);
    t = merge(x.first, split(x.second, 1).second);
  }
  int size(Node *t) { return count(t); }
  bool empty(Node *t) { return !t; }
  Node *makeset() { return (nullptr); }
};
template <class Key>
struct PresidentRandomizedBinarySearchTree : RandomizedBinarySearchTree<Key> {
  using RBST = RandomizedBinarySearchTree<Key>;
  using Node = typename RBST::Node;
  PresidentRandomizedBinarySearchTree(int sz) : RBST(sz) {}
  Node *clone(Node *t) override { return &(RBST::pool[RBST::ptr++] = *t); }
  Node *rebuild(Node *r) { return RBST::build(RBST::dump(r)); }
};
template <class T>
struct OrderedMultiSet : PresidentRandomizedBinarySearchTree<T> {
  using RBST = PresidentRandomizedBinarySearchTree<T>;
  using Node = typename RBST::Node;
  OrderedMultiSet(int sz) : RBST(sz) {}
  T kth_element(Node *t, int k) {
    if (k < RBST::count(t->l))
      return kth_element(t->l, k);
    if (k == RBST::count(t->l))
      return t->key;
    return kth_element(t->r, k - RBST::count(t->l) - 1);
  }
  virtual void insert_key(Node *&t, const T &x) {
    RBST::insert(t, lower_bound(t, x), x);
  }
  void erase_key(Node *&t, const T &x) {
    if (!count(t, x))
      return;
    RBST::erase(t, lower_bound(t, x));
  }
  int count(Node *t, const T &x) {
    return upper_bound(t, x) - lower_bound(t, x);
  }
  int lower_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x <= t->key)
      return lower_bound(t->l, x);
    return lower_bound(t->r, x) + RBST::count(t->l) + 1;
  }
  int upper_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x < t->key)
      return upper_bound(t->l, x);
    return upper_bound(t->r, x) + RBST::count(t->l) + 1;
  }
};
struct CentroidPathDecomposition {
  struct Centroid {
    int ParIndex, ParDepth, Deep;
    vector<int> node;
    Centroid(int idx, int dep, int deep)
        : ParIndex(idx), ParDepth(dep), Deep(deep) {}
    inline size_t size() { return (node.size()); }
    inline int &operator[](int k) { return (node[k]); }
    inline pair<int, int> Up() { return (make_pair(ParIndex, ParDepth)); }
  };
  vector<vector<int>> graph;
  vector<int> SubTreeSize, NextPath;
  vector<int> TreeIndex, TreeDepth;
  vector<Centroid> Centroids;
  void BuildSubTreeSize() {
    stack<pair<int, int>> s;
    s.emplace(0, -1);
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      if (~SubTreeSize[p.first]) {
        NextPath[p.first] = -1;
        for (auto &to : graph[p.first]) {
          if (p.second == to)
            continue;
          SubTreeSize[p.first] += SubTreeSize[to];
          if (NextPath[p.first] == -1 ||
              SubTreeSize[NextPath[p.first]] < SubTreeSize[to]) {
            NextPath[p.first] = to;
          }
        }
      } else {
        s.push(p);
        SubTreeSize[p.first] = 1;
        for (auto &to : graph[p.first]) {
          if (p.second != to)
            s.emplace(to, p.first);
        }
      }
    }
  }
  void BuildPath() {
    stack<pair<int, int>> s;
    Centroids.emplace_back(-1, -1, 0);
    s.emplace(0, -1);
    TreeIndex[0] = 0;
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      TreeDepth[p.first] = (int)Centroids[TreeIndex[p.first]].size();
      for (auto &to : graph[p.first]) {
        if (p.second == to)
          continue;
        if (to == NextPath[p.first]) {
          TreeIndex[to] = TreeIndex[p.first];
        } else {
          TreeIndex[to] = (int)Centroids.size();
          Centroids.emplace_back(TreeIndex[p.first], TreeDepth[p.first],
                                 Centroids[TreeIndex[p.first]].Deep + 1);
        }
        s.emplace(to, p.first);
      }
      Centroids[TreeIndex[p.first]].node.emplace_back(p.first);
    }
  }
  void AddEdge(int x, int y) {
    graph[x].push_back(y);
    graph[y].push_back(x);
  }
  virtual void Build() {
    BuildSubTreeSize();
    BuildPath();
  }
  inline size_t size() { return (Centroids.size()); }
  inline pair<int, int> Information(int idx) {
    return (make_pair(TreeIndex[idx], TreeDepth[idx]));
  }
  inline Centroid &operator[](int k) { return (Centroids[k]); }
  inline int LCA(int a, int b) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    return (Centroids[TreeIdxA][TreeDepthA]);
  }
  inline virtual void query(int a, int b,
                            const function<void(int, int, int)> &f) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        f(TreeIdxA, 0, TreeDepthA + 1);
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        f(TreeIdxB, 0, TreeDepthB + 1);
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    f(TreeIdxA, TreeDepthA, TreeDepthB + 1);
  }
  CentroidPathDecomposition(int SZ) {
    graph.resize(SZ);
    SubTreeSize.assign(SZ, -1);
    NextPath.resize(SZ);
    TreeIndex.resize(SZ);
    TreeDepth.resize(SZ);
  }
};
OrderedMultiSet<int> tree(5000000);
OrderedMultiSet<int>::Node *nodes[100000];
int N, Q, X[100000];
vector<int> g[100000];
int parent[100000];
void dfs(int idx, int par, OrderedMultiSet<int>::Node *par_set) {
  parent[idx] = par;
  tree.insert_key(par_set, X[idx]);
  nodes[idx] = par_set;
  for (auto &to : g[idx])
    if (to != par)
      dfs(to, idx, par_set);
}
int main() {
  scanf("%d %d", &N, &Q);
  CentroidPathDecomposition gg(N);
  vector<int> vs;
  for (int i = 0; i < N; i++) {
    scanf("%d", &X[i]);
    vs.emplace_back(X[i]);
  }
  sort(begin(vs), end(vs));
  vs.erase(unique(begin(vs), end(vs)), end(vs));
  for (int i = 1; i < N; i++) {
    int x, y;
    scanf("%d %d", &x, &y);
    --x, --y;
    g[x].emplace_back(y);
    g[y].emplace_back(x);
    gg.AddEdge(x, y);
  }
  dfs(0, -1, tree.makeset());
  gg.Build();
  for (int i = 0; i < Q; i++) {
    int x, y, z;
    scanf("%d %d %d", &x, &y, &z);
    --x, --y;
    int lca = gg.LCA(x, y);
    int ng = -1, ok = (int)vs.size() - 1;
    auto sum = [&](int v) {
      int ret = 0;
      ret += tree.upper_bound(nodes[x], vs[v]);
      ret += tree.upper_bound(nodes[y], vs[v]);
      ret -= tree.upper_bound(nodes[lca], vs[v]);
      if (lca)
        ret -= tree.upper_bound(nodes[parent[lca]], vs[v]);
      return ret;
    };
    while (ok - ng > 1) {
      int mid = (ok + ng) >> 1;
      if (sum(mid) >= z)
        ok = mid;
      else
        ng = mid;
    }
    printf("%d\n", vs[ok]);
  }
}
accuracy: 1
time_ms: 1080
memory_kb: 135224
final_rank: 17

code_snippet: #include <bits/stdc++.h>
using namespace std;
template <class Key> struct RandomizedBinarySearchTree {
  inline int xor128() {
    static int x = 123456789;
    static int y = 362436069;
    static int z = 521288629;
    static int w = 88675123;
    int t;
    t = x ^ (x << 11);
    x = y;
    y = z;
    z = w;
    return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
  }
  struct Node {
    Node *l, *r;
    int cnt;
    Key key;
    Node() {}
    Node(const Key &k) : cnt(1), key(k), l(nullptr), r(nullptr) {}
  };
  vector<Node> pool;
  int ptr;
  RandomizedBinarySearchTree(int sz) : pool(sz), ptr(0) {}
  inline Node *alloc(const Key &key) { return &(pool[ptr++] = Node(key)); }
  virtual Node *clone(Node *t) { return t; }
  inline int count(const Node *t) { return t ? t->cnt : 0; }
  inline Node *update(Node *t) {
    t->cnt = count(t->l) + count(t->r) + 1;
    return t;
  }
  Node *propagete(Node *t) { return update(clone(t)); }
  Node *merge(Node *l, Node *r) {
    if (!l || !r)
      return l ? l : r;
    if (xor128() % (r->cnt + l->cnt) < r->cnt) {
      l = propagete(l);
      l->r = merge(l->r, r);
      return update(l);
    } else {
      r = propagete(r);
      r->l = merge(l, r->l);
      return update(r);
    }
  }
  pair<Node *, Node *> split(Node *t, int k) {
    if (!t)
      return {t, t};
    t = propagete(t);
    if (k <= count(t->l)) {
      auto s = split(t->l, k);
      t->l = s.second;
      return {s.first, update(t)};
    } else {
      auto s = split(t->r, k - count(t->l) - 1);
      t->r = s.first;
      return {update(t), s.second};
    }
  }
  Node *build(int l, int r, const vector<Key> &v) {
    if (l + 1 >= r)
      return alloc(v[l]);
    return merge(build(l, (l + r) >> 1, v), build((l + r) >> 1, r, v));
  }
  Node *build(const vector<Key> &v) {
    ptr = 0;
    return build(0, (int)v.size(), v);
  }
  void dump(Node *r, typename vector<Key>::iterator &it) {
    if (!r)
      return;
    r = propagete(r);
    dump(r->l, it);
    *it = r->key;
    dump(r->r, ++it);
  }
  vector<Key> dump(Node *r) {
    vector<Key> v((size_t)count(r));
    auto it = begin(v);
    dump(r, it);
    return v;
  }
  string to_string(Node *r) {
    auto s = dump(r);
    string ret;
    for (int i = 0; i < s.size(); i++)
      ret += ", ";
    return (ret);
  }
  void insert(Node *&t, int k, const Key &v) {
    auto x = split(t, k);
    t = merge(merge(x.first, alloc(v)), x.second);
  }
  void erase(Node *&t, int k) {
    auto x = split(t, k);
    t = merge(x.first, split(x.second, 1).second);
  }
  int size(Node *t) { return count(t); }
  bool empty(Node *t) { return !t; }
  Node *makeset() { return (nullptr); }
};
template <class Key>
struct PresidentRandomizedBinarySearchTree : RandomizedBinarySearchTree<Key> {
  using RBST = RandomizedBinarySearchTree<Key>;
  using Node = typename RBST::Node;
  PresidentRandomizedBinarySearchTree(int sz) : RBST(sz) {}
  Node *clone(Node *t) override { return &(RBST::pool[RBST::ptr++] = *t); }
  Node *rebuild(Node *r) { return RBST::build(RBST::dump(r)); }
};
template <class T>
struct OrderedMultiSet : PresidentRandomizedBinarySearchTree<T> {
  using RBST = PresidentRandomizedBinarySearchTree<T>;
  using Node = typename RBST::Node;
  OrderedMultiSet(int sz) : RBST(sz) {}
  T kth_element(Node *t, int k) {
    if (k < RBST::count(t->l))
      return kth_element(t->l, k);
    if (k == RBST::count(t->l))
      return t->key;
    return kth_element(t->r, k - RBST::count(t->l) - 1);
  }
  virtual void insert_key(Node *&t, const T &x) {
    RBST::insert(t, lower_bound(t, x), x);
  }
  void erase_key(Node *&t, const T &x) {
    if (!count(t, x))
      return;
    RBST::erase(t, lower_bound(t, x));
  }
  int count(Node *t, const T &x) {
    return upper_bound(t, x) - lower_bound(t, x);
  }
  int lower_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x <= t->key)
      return lower_bound(t->l, x);
    return lower_bound(t->r, x) + RBST::count(t->l) + 1;
  }
  int upper_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x < t->key)
      return upper_bound(t->l, x);
    return upper_bound(t->r, x) + RBST::count(t->l) + 1;
  }
};
struct CentroidPathDecomposition {
  struct Centroid {
    int ParIndex, ParDepth, Deep;
    vector<int> node;
    Centroid(int idx, int dep, int deep)
        : ParIndex(idx), ParDepth(dep), Deep(deep) {}
    inline size_t size() { return (node.size()); }
    inline int &operator[](int k) { return (node[k]); }
    inline pair<int, int> Up() { return (make_pair(ParIndex, ParDepth)); }
  };
  vector<vector<int>> graph;
  vector<int> SubTreeSize, NextPath;
  vector<int> TreeIndex, TreeDepth;
  vector<Centroid> Centroids;
  void BuildSubTreeSize() {
    stack<pair<int, int>> s;
    s.emplace(0, -1);
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      if (~SubTreeSize[p.first]) {
        NextPath[p.first] = -1;
        for (auto &to : graph[p.first]) {
          if (p.second == to)
            continue;
          SubTreeSize[p.first] += SubTreeSize[to];
          if (NextPath[p.first] == -1 ||
              SubTreeSize[NextPath[p.first]] < SubTreeSize[to]) {
            NextPath[p.first] = to;
          }
        }
      } else {
        s.push(p);
        SubTreeSize[p.first] = 1;
        for (auto &to : graph[p.first]) {
          if (p.second != to)
            s.emplace(to, p.first);
        }
      }
    }
  }
  void BuildPath() {
    stack<pair<int, int>> s;
    Centroids.emplace_back(-1, -1, 0);
    s.emplace(0, -1);
    TreeIndex[0] = 0;
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      TreeDepth[p.first] = (int)Centroids[TreeIndex[p.first]].size();
      for (auto &to : graph[p.first]) {
        if (p.second == to)
          continue;
        if (to == NextPath[p.first]) {
          TreeIndex[to] = TreeIndex[p.first];
        } else {
          TreeIndex[to] = (int)Centroids.size();
          Centroids.emplace_back(TreeIndex[p.first], TreeDepth[p.first],
                                 Centroids[TreeIndex[p.first]].Deep + 1);
        }
        s.emplace(to, p.first);
      }
      Centroids[TreeIndex[p.first]].node.emplace_back(p.first);
    }
  }
  void AddEdge(int x, int y) {
    graph[x].push_back(y);
    graph[y].push_back(x);
  }
  virtual void Build() {
    BuildSubTreeSize();
    BuildPath();
  }
  inline size_t size() { return (Centroids.size()); }
  inline pair<int, int> Information(int idx) {
    return (make_pair(TreeIndex[idx], TreeDepth[idx]));
  }
  inline Centroid &operator[](int k) { return (Centroids[k]); }
  inline int LCA(int a, int b) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    return (Centroids[TreeIdxA][TreeDepthA]);
  }
  inline virtual void query(int a, int b,
                            const function<void(int, int, int)> &f) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        f(TreeIdxA, 0, TreeDepthA + 1);
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        f(TreeIdxB, 0, TreeDepthB + 1);
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    f(TreeIdxA, TreeDepthA, TreeDepthB + 1);
  }
  CentroidPathDecomposition(int SZ) {
    graph.resize(SZ);
    SubTreeSize.assign(SZ, -1);
    NextPath.resize(SZ);
    TreeIndex.resize(SZ);
    TreeDepth.resize(SZ);
  }
};
OrderedMultiSet<int> tree(5000000);
OrderedMultiSet<int>::Node *nodes[100000];
int N, Q, X[100000];
vector<int> g[100000];
int parent[100000];
void dfs(int idx, int par, OrderedMultiSet<int>::Node *par_set) {
  parent[idx] = par;
  tree.insert_key(par_set, X[idx]);
  nodes[idx] = par_set;
  for (auto &to : g[idx])
    if (to != par)
      dfs(to, idx, par_set);
}
int main() {
  scanf("%d %d", &N, &Q);
  CentroidPathDecomposition gg(N);
  vector<int> vs;
  for (int i = 0; i < N; i++) {
    scanf("%d", &X[i]);
    vs.emplace_back(X[i]);
  }
  sort(begin(vs), end(vs));
  vs.erase(unique(begin(vs), end(vs)), end(vs));
  for (int i = 1; i < N; i++) {
    int x, y;
    scanf("%d %d", &x, &y);
    --x, --y;
    g[x].emplace_back(y);
    g[y].emplace_back(x);
    gg.AddEdge(x, y);
  }
  dfs(0, -1, tree.makeset());
  gg.Build();
  for (int i = 0; i < Q; i++) {
    int x, y, z;
    scanf("%d %d %d", &x, &y, &z);
    --x, --y;
    int lca = gg.LCA(x, y);
    int ng = -1, ok = (int)vs.size() - 1;
    auto sum = [&](int v) {
      int ret = 0;
      ret += tree.upper_bound(nodes[x], vs[v]);
      ret += tree.upper_bound(nodes[y], vs[v]);
      ret -= tree.upper_bound(nodes[lca], vs[v]);
      if (lca)
        ret -= tree.upper_bound(nodes[parent[lca]], vs[v]);
      return ret;
    };
    while (ok - ng > 1) {
      int mid = (ok + ng) >> 1;
      if (sum(mid) >= z)
        ok = mid;
      else
        ng = mid;
    }
    printf("%d\n", vs[ok]);
  }
}
accuracy: 1
time_ms: 1040
memory_kb: 135488
final_rank: 15

code_snippet: #include <bits/stdc++.h>
using namespace std;
template <class Key> struct RandomizedBinarySearchTree {
  inline int xor128() {
    static int x = 123456789;
    static int y = 362436069;
    static int z = 521288629;
    static int w = 88675123;
    int t;
    t = x ^ (x << 11);
    x = y;
    y = z;
    z = w;
    return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
  }
  struct Node {
    Node *l, *r;
    int cnt;
    Key key;
    Node() {}
    Node(const Key &k) : cnt(1), key(k), l(nullptr), r(nullptr) {}
  };
  vector<Node> pool;
  int ptr;
  RandomizedBinarySearchTree(int sz) : pool(sz), ptr(0) {}
  inline Node *alloc(const Key &key) { return &(pool[ptr++] = Node(key)); }
  virtual Node *clone(Node *t) { return t; }
  inline int count(const Node *t) { return t ? t->cnt : 0; }
  inline Node *update(Node *t) {
    t->cnt = count(t->l) + count(t->r) + 1;
    return t;
  }
  Node *propagete(Node *t) { return update(clone(t)); }
  Node *merge(Node *l, Node *r) {
    if (!l || !r)
      return l ? l : r;
    if (xor128() % (l->cnt + r->cnt) < r->cnt) {
      l = propagete(l);
      l->r = merge(l->r, r);
      return update(l);
    } else {
      r = propagete(r);
      r->l = merge(l, r->l);
      return update(r);
    }
  }
  pair<Node *, Node *> split(Node *t, int k) {
    if (!t)
      return {t, t};
    t = propagete(t);
    if (k <= count(t->l)) {
      auto s = split(t->l, k);
      t->l = s.second;
      return {s.first, update(t)};
    } else {
      auto s = split(t->r, k - count(t->l) - 1);
      t->r = s.first;
      return {update(t), s.second};
    }
  }
  Node *build(int l, int r, const vector<Key> &v) {
    if (l + 1 >= r)
      return alloc(v[l]);
    return merge(build(l, (l + r) >> 1, v), build((l + r) >> 1, r, v));
  }
  Node *build(const vector<Key> &v) {
    ptr = 0;
    return build(0, (int)v.size(), v);
  }
  void dump(Node *r, typename vector<Key>::iterator &it) {
    if (!r)
      return;
    r = propagete(r);
    dump(r->l, it);
    *it = r->key;
    dump(r->r, ++it);
  }
  vector<Key> dump(Node *r) {
    vector<Key> v((size_t)count(r));
    auto it = begin(v);
    dump(r, it);
    return v;
  }
  string to_string(Node *r) {
    auto s = dump(r);
    string ret;
    for (int i = 0; i < s.size(); i++)
      ret += ", ";
    return (ret);
  }
  void insert(Node *&t, int k, const Key &v) {
    auto x = split(t, k);
    t = merge(merge(x.first, alloc(v)), x.second);
  }
  void erase(Node *&t, int k) {
    auto x = split(t, k);
    t = merge(x.first, split(x.second, 1).second);
  }
  int size(Node *t) { return count(t); }
  bool empty(Node *t) { return !t; }
  Node *makeset() { return (nullptr); }
};
template <class Key>
struct PresidentRandomizedBinarySearchTree : RandomizedBinarySearchTree<Key> {
  using RBST = RandomizedBinarySearchTree<Key>;
  using Node = typename RBST::Node;
  PresidentRandomizedBinarySearchTree(int sz) : RBST(sz) {}
  Node *clone(Node *t) override { return &(RBST::pool[RBST::ptr++] = *t); }
  Node *rebuild(Node *r) { return RBST::build(RBST::dump(r)); }
};
template <class T>
struct OrderedMultiSet : PresidentRandomizedBinarySearchTree<T> {
  using RBST = PresidentRandomizedBinarySearchTree<T>;
  using Node = typename RBST::Node;
  OrderedMultiSet(int sz) : RBST(sz) {}
  T kth_element(Node *t, int k) {
    if (k < RBST::count(t->l))
      return kth_element(t->l, k);
    if (k == RBST::count(t->l))
      return t->key;
    return kth_element(t->r, k - RBST::count(t->l) - 1);
  }
  virtual void insert_key(Node *&t, const T &x) {
    RBST::insert(t, lower_bound(t, x), x);
  }
  void erase_key(Node *&t, const T &x) {
    if (!count(t, x))
      return;
    RBST::erase(t, lower_bound(t, x));
  }
  int count(Node *t, const T &x) {
    return upper_bound(t, x) - lower_bound(t, x);
  }
  int lower_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x <= t->key)
      return lower_bound(t->l, x);
    return lower_bound(t->r, x) + RBST::count(t->l) + 1;
  }
  int upper_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x < t->key)
      return upper_bound(t->l, x);
    return upper_bound(t->r, x) + RBST::count(t->l) + 1;
  }
};
struct CentroidPathDecomposition {
  struct Centroid {
    int ParIndex, ParDepth, Deep;
    vector<int> node;
    Centroid(int idx, int dep, int deep)
        : ParIndex(idx), ParDepth(dep), Deep(deep) {}
    inline size_t size() { return (node.size()); }
    inline int &operator[](int k) { return (node[k]); }
    inline pair<int, int> Up() { return (make_pair(ParIndex, ParDepth)); }
  };
  vector<vector<int>> graph;
  vector<int> SubTreeSize, NextPath;
  vector<int> TreeIndex, TreeDepth;
  vector<Centroid> Centroids;
  void BuildSubTreeSize() {
    stack<pair<int, int>> s;
    s.emplace(0, -1);
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      if (~SubTreeSize[p.first]) {
        NextPath[p.first] = -1;
        for (auto &to : graph[p.first]) {
          if (p.second == to)
            continue;
          SubTreeSize[p.first] += SubTreeSize[to];
          if (NextPath[p.first] == -1 ||
              SubTreeSize[NextPath[p.first]] < SubTreeSize[to]) {
            NextPath[p.first] = to;
          }
        }
      } else {
        s.push(p);
        SubTreeSize[p.first] = 1;
        for (auto &to : graph[p.first]) {
          if (p.second != to)
            s.emplace(to, p.first);
        }
      }
    }
  }
  void BuildPath() {
    stack<pair<int, int>> s;
    Centroids.emplace_back(-1, -1, 0);
    s.emplace(0, -1);
    TreeIndex[0] = 0;
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      TreeDepth[p.first] = (int)Centroids[TreeIndex[p.first]].size();
      for (auto &to : graph[p.first]) {
        if (p.second == to)
          continue;
        if (to == NextPath[p.first]) {
          TreeIndex[to] = TreeIndex[p.first];
        } else {
          TreeIndex[to] = (int)Centroids.size();
          Centroids.emplace_back(TreeIndex[p.first], TreeDepth[p.first],
                                 Centroids[TreeIndex[p.first]].Deep + 1);
        }
        s.emplace(to, p.first);
      }
      Centroids[TreeIndex[p.first]].node.emplace_back(p.first);
    }
  }
  void AddEdge(int x, int y) {
    graph[x].push_back(y);
    graph[y].push_back(x);
  }
  virtual void Build() {
    BuildSubTreeSize();
    BuildPath();
  }
  inline size_t size() { return (Centroids.size()); }
  inline pair<int, int> Information(int idx) {
    return (make_pair(TreeIndex[idx], TreeDepth[idx]));
  }
  inline Centroid &operator[](int k) { return (Centroids[k]); }
  inline int LCA(int a, int b) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    return (Centroids[TreeIdxA][TreeDepthA]);
  }
  inline virtual void query(int a, int b,
                            const function<void(int, int, int)> &f) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        f(TreeIdxA, 0, TreeDepthA + 1);
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        f(TreeIdxB, 0, TreeDepthB + 1);
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    f(TreeIdxA, TreeDepthA, TreeDepthB + 1);
  }
  CentroidPathDecomposition(int SZ) {
    graph.resize(SZ);
    SubTreeSize.assign(SZ, -1);
    NextPath.resize(SZ);
    TreeIndex.resize(SZ);
    TreeDepth.resize(SZ);
  }
};
OrderedMultiSet<int> tree(5000000);
OrderedMultiSet<int>::Node *nodes[100000];
int N, Q, X[100000];
vector<int> g[100000];
int parent[100000];
void dfs(int idx, int par, OrderedMultiSet<int>::Node *par_set) {
  parent[idx] = par;
  tree.insert_key(par_set, X[idx]);
  nodes[idx] = par_set;
  for (auto &to : g[idx])
    if (to != par)
      dfs(to, idx, par_set);
}
int main() {
  scanf("%d %d", &N, &Q);
  CentroidPathDecomposition gg(N);
  vector<int> vs;
  for (int i = 0; i < N; i++) {
    scanf("%d", &X[i]);
    vs.emplace_back(X[i]);
  }
  sort(begin(vs), end(vs));
  vs.erase(unique(begin(vs), end(vs)), end(vs));
  for (int i = 1; i < N; i++) {
    int x, y;
    scanf("%d %d", &x, &y);
    --x, --y;
    g[x].emplace_back(y);
    g[y].emplace_back(x);
    gg.AddEdge(x, y);
  }
  dfs(0, -1, tree.makeset());
  gg.Build();
  for (int i = 0; i < Q; i++) {
    int x, y, z;
    scanf("%d %d %d", &x, &y, &z);
    --x, --y;
    int lca = gg.LCA(x, y);
    int ng = -1, ok = (int)vs.size() - 1;
    auto sum = [&](int v) {
      int ret = 0;
      ret += tree.upper_bound(nodes[x], vs[v]);
      ret += tree.upper_bound(nodes[y], vs[v]);
      ret -= tree.upper_bound(nodes[lca], vs[v]);
      if (lca)
        ret -= tree.upper_bound(nodes[parent[lca]], vs[v]);
      return ret;
    };
    while (ok - ng > 1) {
      int mid = (ok + ng) >> 1;
      if (sum(mid) >= z)
        ok = mid;
      else
        ng = mid;
    }
    printf("%d\n", vs[ok]);
  }
}
accuracy: 1
time_ms: 1050
memory_kb: 136032
final_rank: 16

code_snippet: #include <bits/stdc++.h>
using namespace std;
template <class Key> struct RandomizedBinarySearchTree {
  inline int xor128() {
    static int x = 123456789;
    static int y = 362436069;
    static int z = 521288629;
    static int w = 88675123;
    int t;
    t = x ^ (x << 11);
    x = y;
    y = z;
    z = w;
    return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
  }
  struct Node {
    Node *l, *r;
    int cnt;
    Key key;
    Node() {}
    Node(const Key &k) : cnt(1), key(k), l(nullptr), r(nullptr) {}
  };
  vector<Node> pool;
  int ptr;
  RandomizedBinarySearchTree(int sz) : pool(sz), ptr(0) {}
  inline Node *alloc(const Key &key) { return &(pool[ptr++] = Node(key)); }
  virtual Node *clone(Node *t) { return t; }
  inline int count(const Node *t) { return t ? t->cnt : 0; }
  inline Node *update(Node *t) {
    t->cnt = count(t->l) + count(t->r) + 1;
    return t;
  }
  Node *propagete(Node *t) { return update(clone(t)); }
  Node *merge(Node *l, Node *r) {
    if (!l || !r)
      return l ? l : r;
    if (xor128() % (l->cnt + r->cnt) != l->cnt) {
      l = propagete(l);
      l->r = merge(l->r, r);
      return update(l);
    } else {
      r = propagete(r);
      r->l = merge(l, r->l);
      return update(r);
    }
  }
  pair<Node *, Node *> split(Node *t, int k) {
    if (!t)
      return {t, t};
    t = propagete(t);
    if (k <= count(t->l)) {
      auto s = split(t->l, k);
      t->l = s.second;
      return {s.first, update(t)};
    } else {
      auto s = split(t->r, k - count(t->l) - 1);
      t->r = s.first;
      return {update(t), s.second};
    }
  }
  Node *build(int l, int r, const vector<Key> &v) {
    if (l + 1 >= r)
      return alloc(v[l]);
    return merge(build(l, (l + r) >> 1, v), build((l + r) >> 1, r, v));
  }
  Node *build(const vector<Key> &v) {
    ptr = 0;
    return build(0, (int)v.size(), v);
  }
  void dump(Node *r, typename vector<Key>::iterator &it) {
    if (!r)
      return;
    r = propagete(r);
    dump(r->l, it);
    *it = r->key;
    dump(r->r, ++it);
  }
  vector<Key> dump(Node *r) {
    vector<Key> v((size_t)count(r));
    auto it = begin(v);
    dump(r, it);
    return v;
  }
  string to_string(Node *r) {
    auto s = dump(r);
    string ret;
    for (int i = 0; i < s.size(); i++)
      ret += ", ";
    return (ret);
  }
  void insert(Node *&t, int k, const Key &v) {
    auto x = split(t, k);
    t = merge(merge(x.first, alloc(v)), x.second);
  }
  void erase(Node *&t, int k) {
    auto x = split(t, k);
    t = merge(x.first, split(x.second, 1).second);
  }
  int size(Node *t) { return count(t); }
  bool empty(Node *t) { return !t; }
  Node *makeset() { return (nullptr); }
};
template <class Key>
struct PresidentRandomizedBinarySearchTree : RandomizedBinarySearchTree<Key> {
  using RBST = RandomizedBinarySearchTree<Key>;
  using Node = typename RBST::Node;
  PresidentRandomizedBinarySearchTree(int sz) : RBST(sz) {}
  Node *clone(Node *t) override { return &(RBST::pool[RBST::ptr++] = *t); }
  Node *rebuild(Node *r) { return RBST::build(RBST::dump(r)); }
};
template <class T>
struct OrderedMultiSet : PresidentRandomizedBinarySearchTree<T> {
  using RBST = PresidentRandomizedBinarySearchTree<T>;
  using Node = typename RBST::Node;
  OrderedMultiSet(int sz) : RBST(sz) {}
  T kth_element(Node *t, int k) {
    if (k < RBST::count(t->l))
      return kth_element(t->l, k);
    if (k == RBST::count(t->l))
      return t->key;
    return kth_element(t->r, k - RBST::count(t->l) - 1);
  }
  virtual void insert_key(Node *&t, const T &x) {
    RBST::insert(t, lower_bound(t, x), x);
  }
  void erase_key(Node *&t, const T &x) {
    if (!count(t, x))
      return;
    RBST::erase(t, lower_bound(t, x));
  }
  int count(Node *t, const T &x) {
    return upper_bound(t, x) - lower_bound(t, x);
  }
  int lower_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x <= t->key)
      return lower_bound(t->l, x);
    return lower_bound(t->r, x) + RBST::count(t->l) + 1;
  }
  int upper_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x < t->key)
      return upper_bound(t->l, x);
    return upper_bound(t->r, x) + RBST::count(t->l) + 1;
  }
};
struct CentroidPathDecomposition {
  struct Centroid {
    int ParIndex, ParDepth, Deep;
    vector<int> node;
    Centroid(int idx, int dep, int deep)
        : ParIndex(idx), ParDepth(dep), Deep(deep) {}
    inline size_t size() { return (node.size()); }
    inline int &operator[](int k) { return (node[k]); }
    inline pair<int, int> Up() { return (make_pair(ParIndex, ParDepth)); }
  };
  vector<vector<int>> graph;
  vector<int> SubTreeSize, NextPath;
  vector<int> TreeIndex, TreeDepth;
  vector<Centroid> Centroids;
  void BuildSubTreeSize() {
    stack<pair<int, int>> s;
    s.emplace(0, -1);
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      if (~SubTreeSize[p.first]) {
        NextPath[p.first] = -1;
        for (auto &to : graph[p.first]) {
          if (p.second == to)
            continue;
          SubTreeSize[p.first] += SubTreeSize[to];
          if (NextPath[p.first] == -1 ||
              SubTreeSize[NextPath[p.first]] < SubTreeSize[to]) {
            NextPath[p.first] = to;
          }
        }
      } else {
        s.push(p);
        SubTreeSize[p.first] = 1;
        for (auto &to : graph[p.first]) {
          if (p.second != to)
            s.emplace(to, p.first);
        }
      }
    }
  }
  void BuildPath() {
    stack<pair<int, int>> s;
    Centroids.emplace_back(-1, -1, 0);
    s.emplace(0, -1);
    TreeIndex[0] = 0;
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      TreeDepth[p.first] = (int)Centroids[TreeIndex[p.first]].size();
      for (auto &to : graph[p.first]) {
        if (p.second == to)
          continue;
        if (to == NextPath[p.first]) {
          TreeIndex[to] = TreeIndex[p.first];
        } else {
          TreeIndex[to] = (int)Centroids.size();
          Centroids.emplace_back(TreeIndex[p.first], TreeDepth[p.first],
                                 Centroids[TreeIndex[p.first]].Deep + 1);
        }
        s.emplace(to, p.first);
      }
      Centroids[TreeIndex[p.first]].node.emplace_back(p.first);
    }
  }
  void AddEdge(int x, int y) {
    graph[x].push_back(y);
    graph[y].push_back(x);
  }
  virtual void Build() {
    BuildSubTreeSize();
    BuildPath();
  }
  inline size_t size() { return (Centroids.size()); }
  inline pair<int, int> Information(int idx) {
    return (make_pair(TreeIndex[idx], TreeDepth[idx]));
  }
  inline Centroid &operator[](int k) { return (Centroids[k]); }
  inline int LCA(int a, int b) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    return (Centroids[TreeIdxA][TreeDepthA]);
  }
  inline virtual void query(int a, int b,
                            const function<void(int, int, int)> &f) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        f(TreeIdxA, 0, TreeDepthA + 1);
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        f(TreeIdxB, 0, TreeDepthB + 1);
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    f(TreeIdxA, TreeDepthA, TreeDepthB + 1);
  }
  CentroidPathDecomposition(int SZ) {
    graph.resize(SZ);
    SubTreeSize.assign(SZ, -1);
    NextPath.resize(SZ);
    TreeIndex.resize(SZ);
    TreeDepth.resize(SZ);
  }
};
OrderedMultiSet<int> tree(5000000);
OrderedMultiSet<int>::Node *nodes[100000];
int N, Q, X[100000];
vector<int> g[100000];
int parent[100000];
void dfs(int idx, int par, OrderedMultiSet<int>::Node *par_set) {
  parent[idx] = par;
  tree.insert_key(par_set, X[idx]);
  nodes[idx] = par_set;
  for (auto &to : g[idx])
    if (to != par)
      dfs(to, idx, par_set);
}
int main() {
  scanf("%d %d", &N, &Q);
  CentroidPathDecomposition gg(N);
  vector<int> vs;
  for (int i = 0; i < N; i++) {
    scanf("%d", &X[i]);
    vs.emplace_back(X[i]);
  }
  sort(begin(vs), end(vs));
  vs.erase(unique(begin(vs), end(vs)), end(vs));
  for (int i = 1; i < N; i++) {
    int x, y;
    scanf("%d %d", &x, &y);
    --x, --y;
    g[x].emplace_back(y);
    g[y].emplace_back(x);
    gg.AddEdge(x, y);
  }
  dfs(0, -1, tree.makeset());
  gg.Build();
  for (int i = 0; i < Q; i++) {
    int x, y, z;
    scanf("%d %d %d", &x, &y, &z);
    --x, --y;
    int lca = gg.LCA(x, y);
    int ng = -1, ok = (int)vs.size() - 1;
    auto sum = [&](int v) {
      int ret = 0;
      ret += tree.upper_bound(nodes[x], vs[v]);
      ret += tree.upper_bound(nodes[y], vs[v]);
      ret -= tree.upper_bound(nodes[lca], vs[v]);
      if (lca)
        ret -= tree.upper_bound(nodes[parent[lca]], vs[v]);
      return ret;
    };
    while (ok - ng > 1) {
      int mid = (ok + ng) >> 1;
      if (sum(mid) >= z)
        ok = mid;
      else
        ng = mid;
    }
    printf("%d\n", vs[ok]);
  }
}
accuracy: 0.7096774193548387
time_ms: 380
memory_kb: 107812
final_rank: 18

code_snippet: #include <bits/stdc++.h>
using namespace std;
template <class Key> struct RandomizedBinarySearchTree {
  inline int xor128() {
    static int x = 123456789;
    static int y = 362436069;
    static int z = 521288629;
    static int w = 88675123;
    int t;
    t = x ^ (x << 11);
    x = y;
    y = z;
    z = w;
    return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
  }
  struct Node {
    Node *l, *r;
    int cnt;
    Key key;
    Node() {}
    Node(const Key &k) : cnt(1), key(k), l(nullptr), r(nullptr) {}
  };
  vector<Node> pool;
  int ptr;
  RandomizedBinarySearchTree(int sz) : pool(sz), ptr(0) {}
  inline Node *alloc(const Key &key) { return &(pool[ptr++] = Node(key)); }
  virtual Node *clone(Node *t) { return t; }
  inline int count(const Node *t) { return t ? t->cnt : 0; }
  inline Node *update(Node *t) {
    t->cnt = count(t->l) + count(t->r) + 1;
    return t;
  }
  Node *propagete(Node *t) { return update(clone(t)); }
  Node *merge(Node *l, Node *r) {
    if (!l || !r)
      return l ? l : r;
    if ((xor128() % (l->cnt + r->cnt)) < l->cnt) {
      l = propagete(l);
      l->r = merge(l->r, r);
      return update(l);
    } else {
      r = propagete(r);
      r->l = merge(l, r->l);
      return update(r);
    }
  }
  pair<Node *, Node *> split(Node *t, int k) {
    if (!t)
      return {t, t};
    t = propagete(t);
    if (k <= count(t->l)) {
      auto s = split(t->l, k);
      t->l = s.second;
      return {s.first, update(t)};
    } else {
      auto s = split(t->r, k - count(t->l) - 1);
      t->r = s.first;
      return {update(t), s.second};
    }
  }
  Node *build(int l, int r, const vector<Key> &v) {
    if (l + 1 >= r)
      return alloc(v[l]);
    return merge(build(l, (l + r) >> 1, v), build((l + r) >> 1, r, v));
  }
  Node *build(const vector<Key> &v) {
    ptr = 0;
    return build(0, (int)v.size(), v);
  }
  void dump(Node *r, typename vector<Key>::iterator &it) {
    if (!r)
      return;
    r = propagete(r);
    dump(r->l, it);
    *it = r->key;
    dump(r->r, ++it);
  }
  vector<Key> dump(Node *r) {
    vector<Key> v((size_t)count(r));
    auto it = begin(v);
    dump(r, it);
    return v;
  }
  string to_string(Node *r) {
    auto s = dump(r);
    string ret;
    for (int i = 0; i < s.size(); i++)
      ret += ", ";
    return (ret);
  }
  void insert(Node *&t, int k, const Key &v) {
    auto x = split(t, k);
    t = merge(merge(x.first, alloc(v)), x.second);
  }
  void erase(Node *&t, int k) {
    auto x = split(t, k);
    t = merge(x.first, split(x.second, 1).second);
  }
  int size(Node *t) { return count(t); }
  bool empty(Node *t) { return !t; }
  Node *makeset() { return (nullptr); }
};
template <class Key>
struct PresidentRandomizedBinarySearchTree : RandomizedBinarySearchTree<Key> {
  using RBST = RandomizedBinarySearchTree<Key>;
  using Node = typename RBST::Node;
  PresidentRandomizedBinarySearchTree(int sz) : RBST(sz) {}
  Node *clone(Node *t) override { return &(RBST::pool[RBST::ptr++] = *t); }
  Node *rebuild(Node *r) { return RBST::build(RBST::dump(r)); }
};
template <class T>
struct OrderedMultiSet : PresidentRandomizedBinarySearchTree<T> {
  using RBST = PresidentRandomizedBinarySearchTree<T>;
  using Node = typename RBST::Node;
  OrderedMultiSet(int sz) : RBST(sz) {}
  T kth_element(Node *t, int k) {
    if (k < RBST::count(t->l))
      return kth_element(t->l, k);
    if (k == RBST::count(t->l))
      return t->key;
    return kth_element(t->r, k - RBST::count(t->l) - 1);
  }
  virtual void insert_key(Node *&t, const T &x) {
    RBST::insert(t, lower_bound(t, x), x);
  }
  void erase_key(Node *&t, const T &x) {
    if (!count(t, x))
      return;
    RBST::erase(t, lower_bound(t, x));
  }
  int count(Node *t, const T &x) {
    return upper_bound(t, x) - lower_bound(t, x);
  }
  int lower_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x <= t->key)
      return lower_bound(t->l, x);
    return lower_bound(t->r, x) + RBST::count(t->l) + 1;
  }
  int upper_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x < t->key)
      return upper_bound(t->l, x);
    return upper_bound(t->r, x) + RBST::count(t->l) + 1;
  }
};
struct CentroidPathDecomposition {
  struct Centroid {
    int ParIndex, ParDepth, Deep;
    vector<int> node;
    Centroid(int idx, int dep, int deep)
        : ParIndex(idx), ParDepth(dep), Deep(deep) {}
    inline size_t size() { return (node.size()); }
    inline int &operator[](int k) { return (node[k]); }
    inline pair<int, int> Up() { return (make_pair(ParIndex, ParDepth)); }
  };
  vector<vector<int>> graph;
  vector<int> SubTreeSize, NextPath;
  vector<int> TreeIndex, TreeDepth;
  vector<Centroid> Centroids;
  void BuildSubTreeSize() {
    stack<pair<int, int>> s;
    s.emplace(0, -1);
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      if (~SubTreeSize[p.first]) {
        NextPath[p.first] = -1;
        for (auto &to : graph[p.first]) {
          if (p.second == to)
            continue;
          SubTreeSize[p.first] += SubTreeSize[to];
          if (NextPath[p.first] == -1 ||
              SubTreeSize[NextPath[p.first]] < SubTreeSize[to]) {
            NextPath[p.first] = to;
          }
        }
      } else {
        s.push(p);
        SubTreeSize[p.first] = 1;
        for (auto &to : graph[p.first]) {
          if (p.second != to)
            s.emplace(to, p.first);
        }
      }
    }
  }
  void BuildPath() {
    stack<pair<int, int>> s;
    Centroids.emplace_back(-1, -1, 0);
    s.emplace(0, -1);
    TreeIndex[0] = 0;
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      TreeDepth[p.first] = (int)Centroids[TreeIndex[p.first]].size();
      for (auto &to : graph[p.first]) {
        if (p.second == to)
          continue;
        if (to == NextPath[p.first]) {
          TreeIndex[to] = TreeIndex[p.first];
        } else {
          TreeIndex[to] = (int)Centroids.size();
          Centroids.emplace_back(TreeIndex[p.first], TreeDepth[p.first],
                                 Centroids[TreeIndex[p.first]].Deep + 1);
        }
        s.emplace(to, p.first);
      }
      Centroids[TreeIndex[p.first]].node.emplace_back(p.first);
    }
  }
  void AddEdge(int x, int y) {
    graph[x].push_back(y);
    graph[y].push_back(x);
  }
  virtual void Build() {
    BuildSubTreeSize();
    BuildPath();
  }
  inline size_t size() { return (Centroids.size()); }
  inline pair<int, int> Information(int idx) {
    return (make_pair(TreeIndex[idx], TreeDepth[idx]));
  }
  inline Centroid &operator[](int k) { return (Centroids[k]); }
  inline int LCA(int a, int b) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    return (Centroids[TreeIdxA][TreeDepthA]);
  }
  inline virtual void query(int a, int b,
                            const function<void(int, int, int)> &f) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        f(TreeIdxA, 0, TreeDepthA + 1);
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        f(TreeIdxB, 0, TreeDepthB + 1);
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    f(TreeIdxA, TreeDepthA, TreeDepthB + 1);
  }
  CentroidPathDecomposition(int SZ) {
    graph.resize(SZ);
    SubTreeSize.assign(SZ, -1);
    NextPath.resize(SZ);
    TreeIndex.resize(SZ);
    TreeDepth.resize(SZ);
  }
};
OrderedMultiSet<int> tree(5000000);
OrderedMultiSet<int>::Node *nodes[100000];
int N, Q, X[100000];
vector<int> g[100000];
int parent[100000];
void dfs(int idx, int par, OrderedMultiSet<int>::Node *par_set) {
  parent[idx] = par;
  tree.insert_key(par_set, X[idx]);
  nodes[idx] = par_set;
  for (auto &to : g[idx])
    if (to != par)
      dfs(to, idx, par_set);
}
int main() {
  scanf("%d %d", &N, &Q);
  CentroidPathDecomposition gg(N);
  vector<int> vs;
  for (int i = 0; i < N; i++) {
    scanf("%d", &X[i]);
    vs.emplace_back(X[i]);
  }
  sort(begin(vs), end(vs));
  vs.erase(unique(begin(vs), end(vs)), end(vs));
  for (int i = 1; i < N; i++) {
    int x, y;
    scanf("%d %d", &x, &y);
    --x, --y;
    g[x].emplace_back(y);
    g[y].emplace_back(x);
    gg.AddEdge(x, y);
  }
  dfs(0, -1, tree.makeset());
  gg.Build();
  for (int i = 0; i < Q; i++) {
    int x, y, z;
    scanf("%d %d %d", &x, &y, &z);
    --x, --y;
    int lca = gg.LCA(x, y);
    int ng = -1, ok = (int)vs.size() - 1;
    auto sum = [&](int v) {
      int ret = 0;
      ret += tree.upper_bound(nodes[x], vs[v]);
      ret += tree.upper_bound(nodes[y], vs[v]);
      ret -= tree.upper_bound(nodes[lca], vs[v]);
      if (lca)
        ret -= tree.upper_bound(nodes[parent[lca]], vs[v]);
      return ret;
    };
    while (ok - ng > 1) {
      int mid = (ok + ng) >> 1;
      if (sum(mid) >= z)
        ok = mid;
      else
        ng = mid;
    }
    printf("%d\n", vs[ok]);
  }
}
accuracy: 1
time_ms: 860
memory_kb: 133224
final_rank: 12

code_snippet: #include <bits/stdc++.h>
using namespace std;
template <class Key> struct RandomizedBinarySearchTree {
  inline int xor128() {
    static int x = 123456789;
    static int y = 362436069;
    static int z = 521288629;
    static int w = 88675123;
    int t;
    t = x ^ (x << 11);
    x = y;
    y = z;
    z = w;
    return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
  }
  struct Node {
    Node *l, *r;
    int cnt;
    Key key;
    Node() {}
    Node(const Key &k) : cnt(1), key(k), l(nullptr), r(nullptr) {}
  };
  vector<Node> pool;
  int ptr;
  RandomizedBinarySearchTree(int sz) : pool(sz), ptr(0) {}
  inline Node *alloc(const Key &key) { return &(pool[ptr++] = Node(key)); }
  virtual Node *clone(Node *t) { return t; }
  inline int count(const Node *t) { return t ? t->cnt : 0; }
  inline Node *update(Node *t) {
    t->cnt = count(t->l) + count(t->r) + 1;
    return t;
  }
  Node *propagete(Node *t) { return update(clone(t)); }
  Node *merge(Node *l, Node *r) {
    if (!l || !r)
      return l ? l : r;
    if (xor128() % (l->cnt + r->cnt) == l->cnt) {
      l = propagete(l);
      l->r = merge(l->r, r);
      return update(l);
    } else {
      r = propagete(r);
      r->l = merge(l, r->l);
      return update(r);
    }
  }
  pair<Node *, Node *> split(Node *t, int k) {
    if (!t)
      return {t, t};
    t = propagete(t);
    if (k <= count(t->l)) {
      auto s = split(t->l, k);
      t->l = s.second;
      return {s.first, update(t)};
    } else {
      auto s = split(t->r, k - count(t->l) - 1);
      t->r = s.first;
      return {update(t), s.second};
    }
  }
  Node *build(int l, int r, const vector<Key> &v) {
    if (l + 1 >= r)
      return alloc(v[l]);
    return merge(build(l, (l + r) >> 1, v), build((l + r) >> 1, r, v));
  }
  Node *build(const vector<Key> &v) {
    ptr = 0;
    return build(0, (int)v.size(), v);
  }
  void dump(Node *r, typename vector<Key>::iterator &it) {
    if (!r)
      return;
    r = propagete(r);
    dump(r->l, it);
    *it = r->key;
    dump(r->r, ++it);
  }
  vector<Key> dump(Node *r) {
    vector<Key> v((size_t)count(r));
    auto it = begin(v);
    dump(r, it);
    return v;
  }
  string to_string(Node *r) {
    auto s = dump(r);
    string ret;
    for (int i = 0; i < s.size(); i++)
      ret += ", ";
    return (ret);
  }
  void insert(Node *&t, int k, const Key &v) {
    auto x = split(t, k);
    t = merge(merge(x.first, alloc(v)), x.second);
  }
  void erase(Node *&t, int k) {
    auto x = split(t, k);
    t = merge(x.first, split(x.second, 1).second);
  }
  int size(Node *t) { return count(t); }
  bool empty(Node *t) { return !t; }
  Node *makeset() { return (nullptr); }
};
template <class Key>
struct PresidentRandomizedBinarySearchTree : RandomizedBinarySearchTree<Key> {
  using RBST = RandomizedBinarySearchTree<Key>;
  using Node = typename RBST::Node;
  PresidentRandomizedBinarySearchTree(int sz) : RBST(sz) {}
  Node *clone(Node *t) override { return &(RBST::pool[RBST::ptr++] = *t); }
  Node *rebuild(Node *r) { return RBST::build(RBST::dump(r)); }
};
template <class T>
struct OrderedMultiSet : PresidentRandomizedBinarySearchTree<T> {
  using RBST = PresidentRandomizedBinarySearchTree<T>;
  using Node = typename RBST::Node;
  OrderedMultiSet(int sz) : RBST(sz) {}
  T kth_element(Node *t, int k) {
    if (k < RBST::count(t->l))
      return kth_element(t->l, k);
    if (k == RBST::count(t->l))
      return t->key;
    return kth_element(t->r, k - RBST::count(t->l) - 1);
  }
  virtual void insert_key(Node *&t, const T &x) {
    RBST::insert(t, lower_bound(t, x), x);
  }
  void erase_key(Node *&t, const T &x) {
    if (!count(t, x))
      return;
    RBST::erase(t, lower_bound(t, x));
  }
  int count(Node *t, const T &x) {
    return upper_bound(t, x) - lower_bound(t, x);
  }
  int lower_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x <= t->key)
      return lower_bound(t->l, x);
    return lower_bound(t->r, x) + RBST::count(t->l) + 1;
  }
  int upper_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x < t->key)
      return upper_bound(t->l, x);
    return upper_bound(t->r, x) + RBST::count(t->l) + 1;
  }
};
struct CentroidPathDecomposition {
  struct Centroid {
    int ParIndex, ParDepth, Deep;
    vector<int> node;
    Centroid(int idx, int dep, int deep)
        : ParIndex(idx), ParDepth(dep), Deep(deep) {}
    inline size_t size() { return (node.size()); }
    inline int &operator[](int k) { return (node[k]); }
    inline pair<int, int> Up() { return (make_pair(ParIndex, ParDepth)); }
  };
  vector<vector<int>> graph;
  vector<int> SubTreeSize, NextPath;
  vector<int> TreeIndex, TreeDepth;
  vector<Centroid> Centroids;
  void BuildSubTreeSize() {
    stack<pair<int, int>> s;
    s.emplace(0, -1);
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      if (~SubTreeSize[p.first]) {
        NextPath[p.first] = -1;
        for (auto &to : graph[p.first]) {
          if (p.second == to)
            continue;
          SubTreeSize[p.first] += SubTreeSize[to];
          if (NextPath[p.first] == -1 ||
              SubTreeSize[NextPath[p.first]] < SubTreeSize[to]) {
            NextPath[p.first] = to;
          }
        }
      } else {
        s.push(p);
        SubTreeSize[p.first] = 1;
        for (auto &to : graph[p.first]) {
          if (p.second != to)
            s.emplace(to, p.first);
        }
      }
    }
  }
  void BuildPath() {
    stack<pair<int, int>> s;
    Centroids.emplace_back(-1, -1, 0);
    s.emplace(0, -1);
    TreeIndex[0] = 0;
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      TreeDepth[p.first] = (int)Centroids[TreeIndex[p.first]].size();
      for (auto &to : graph[p.first]) {
        if (p.second == to)
          continue;
        if (to == NextPath[p.first]) {
          TreeIndex[to] = TreeIndex[p.first];
        } else {
          TreeIndex[to] = (int)Centroids.size();
          Centroids.emplace_back(TreeIndex[p.first], TreeDepth[p.first],
                                 Centroids[TreeIndex[p.first]].Deep + 1);
        }
        s.emplace(to, p.first);
      }
      Centroids[TreeIndex[p.first]].node.emplace_back(p.first);
    }
  }
  void AddEdge(int x, int y) {
    graph[x].push_back(y);
    graph[y].push_back(x);
  }
  virtual void Build() {
    BuildSubTreeSize();
    BuildPath();
  }
  inline size_t size() { return (Centroids.size()); }
  inline pair<int, int> Information(int idx) {
    return (make_pair(TreeIndex[idx], TreeDepth[idx]));
  }
  inline Centroid &operator[](int k) { return (Centroids[k]); }
  inline int LCA(int a, int b) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    return (Centroids[TreeIdxA][TreeDepthA]);
  }
  inline virtual void query(int a, int b,
                            const function<void(int, int, int)> &f) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        f(TreeIdxA, 0, TreeDepthA + 1);
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        f(TreeIdxB, 0, TreeDepthB + 1);
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    f(TreeIdxA, TreeDepthA, TreeDepthB + 1);
  }
  CentroidPathDecomposition(int SZ) {
    graph.resize(SZ);
    SubTreeSize.assign(SZ, -1);
    NextPath.resize(SZ);
    TreeIndex.resize(SZ);
    TreeDepth.resize(SZ);
  }
};
OrderedMultiSet<int> tree(5000000);
OrderedMultiSet<int>::Node *nodes[100000];
int N, Q, X[100000];
vector<int> g[100000];
int parent[100000];
void dfs(int idx, int par, OrderedMultiSet<int>::Node *par_set) {
  parent[idx] = par;
  tree.insert_key(par_set, X[idx]);
  nodes[idx] = par_set;
  for (auto &to : g[idx])
    if (to != par)
      dfs(to, idx, par_set);
}
int main() {
  scanf("%d %d", &N, &Q);
  CentroidPathDecomposition gg(N);
  vector<int> vs;
  for (int i = 0; i < N; i++) {
    scanf("%d", &X[i]);
    vs.emplace_back(X[i]);
  }
  sort(begin(vs), end(vs));
  vs.erase(unique(begin(vs), end(vs)), end(vs));
  for (int i = 1; i < N; i++) {
    int x, y;
    scanf("%d %d", &x, &y);
    --x, --y;
    g[x].emplace_back(y);
    g[y].emplace_back(x);
    gg.AddEdge(x, y);
  }
  dfs(0, -1, tree.makeset());
  gg.Build();
  for (int i = 0; i < Q; i++) {
    int x, y, z;
    scanf("%d %d %d", &x, &y, &z);
    --x, --y;
    int lca = gg.LCA(x, y);
    int ng = -1, ok = (int)vs.size() - 1;
    auto sum = [&](int v) {
      int ret = 0;
      ret += tree.upper_bound(nodes[x], vs[v]);
      ret += tree.upper_bound(nodes[y], vs[v]);
      ret -= tree.upper_bound(nodes[lca], vs[v]);
      if (lca)
        ret -= tree.upper_bound(nodes[parent[lca]], vs[v]);
      return ret;
    };
    while (ok - ng > 1) {
      int mid = (ok + ng) >> 1;
      if (sum(mid) >= z)
        ok = mid;
      else
        ng = mid;
    }
    printf("%d\n", vs[ok]);
  }
}
accuracy: 0.7096774193548387
time_ms: 360
memory_kb: 138016
final_rank: 19

code_snippet: #include <bits/stdc++.h>
using namespace std;
template <class Key> struct RandomizedBinarySearchTree {
  inline int xor128() {
    static int x = 123456789;
    static int y = 362436069;
    static int z = 521288629;
    static int w = 88675123;
    int t;
    t = x ^ (x << 11);
    x = y;
    y = z;
    z = w;
    return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
  }
  struct Node {
    Node *l, *r;
    int cnt;
    Key key;
    Node() {}
    Node(const Key &k) : cnt(1), key(k), l(nullptr), r(nullptr) {}
  };
  vector<Node> pool;
  int ptr;
  RandomizedBinarySearchTree(int sz) : pool(sz), ptr(0) {}
  inline Node *alloc(const Key &key) { return &(pool[ptr++] = Node(key)); }
  virtual Node *clone(Node *t) { return t; }
  inline int count(const Node *t) { return t ? t->cnt : 0; }
  inline Node *update(Node *t) {
    t->cnt = count(t->l) + count(t->r) + 1;
    return t;
  }
  Node *propagete(Node *t) { return update(clone(t)); }
  Node *merge(Node *l, Node *r) {
    if (!l || !r)
      return l ? l : r;
    if (xor128() % l->cnt < r->cnt) {
      l = propagete(l);
      l->r = merge(l->r, r);
      return update(l);
    } else {
      r = propagete(r);
      r->l = merge(l, r->l);
      return update(r);
    }
  }
  pair<Node *, Node *> split(Node *t, int k) {
    if (!t)
      return {t, t};
    t = propagete(t);
    if (k <= count(t->l)) {
      auto s = split(t->l, k);
      t->l = s.second;
      return {s.first, update(t)};
    } else {
      auto s = split(t->r, k - count(t->l) - 1);
      t->r = s.first;
      return {update(t), s.second};
    }
  }
  Node *build(int l, int r, const vector<Key> &v) {
    if (l + 1 >= r)
      return alloc(v[l]);
    return merge(build(l, (l + r) >> 1, v), build((l + r) >> 1, r, v));
  }
  Node *build(const vector<Key> &v) {
    ptr = 0;
    return build(0, (int)v.size(), v);
  }
  void dump(Node *r, typename vector<Key>::iterator &it) {
    if (!r)
      return;
    r = propagete(r);
    dump(r->l, it);
    *it = r->key;
    dump(r->r, ++it);
  }
  vector<Key> dump(Node *r) {
    vector<Key> v((size_t)count(r));
    auto it = begin(v);
    dump(r, it);
    return v;
  }
  string to_string(Node *r) {
    auto s = dump(r);
    string ret;
    for (int i = 0; i < s.size(); i++)
      ret += ", ";
    return (ret);
  }
  void insert(Node *&t, int k, const Key &v) {
    auto x = split(t, k);
    t = merge(merge(x.first, alloc(v)), x.second);
  }
  void erase(Node *&t, int k) {
    auto x = split(t, k);
    t = merge(x.first, split(x.second, 1).second);
  }
  int size(Node *t) { return count(t); }
  bool empty(Node *t) { return !t; }
  Node *makeset() { return (nullptr); }
};
template <class Key>
struct PresidentRandomizedBinarySearchTree : RandomizedBinarySearchTree<Key> {
  using RBST = RandomizedBinarySearchTree<Key>;
  using Node = typename RBST::Node;
  PresidentRandomizedBinarySearchTree(int sz) : RBST(sz) {}
  Node *clone(Node *t) override { return &(RBST::pool[RBST::ptr++] = *t); }
  Node *rebuild(Node *r) { return RBST::build(RBST::dump(r)); }
};
template <class T>
struct OrderedMultiSet : PresidentRandomizedBinarySearchTree<T> {
  using RBST = PresidentRandomizedBinarySearchTree<T>;
  using Node = typename RBST::Node;
  OrderedMultiSet(int sz) : RBST(sz) {}
  T kth_element(Node *t, int k) {
    if (k < RBST::count(t->l))
      return kth_element(t->l, k);
    if (k == RBST::count(t->l))
      return t->key;
    return kth_element(t->r, k - RBST::count(t->l) - 1);
  }
  virtual void insert_key(Node *&t, const T &x) {
    RBST::insert(t, lower_bound(t, x), x);
  }
  void erase_key(Node *&t, const T &x) {
    if (!count(t, x))
      return;
    RBST::erase(t, lower_bound(t, x));
  }
  int count(Node *t, const T &x) {
    return upper_bound(t, x) - lower_bound(t, x);
  }
  int lower_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x <= t->key)
      return lower_bound(t->l, x);
    return lower_bound(t->r, x) + RBST::count(t->l) + 1;
  }
  int upper_bound(Node *t, const T &x) {
    if (!t)
      return 0;
    if (x < t->key)
      return upper_bound(t->l, x);
    return upper_bound(t->r, x) + RBST::count(t->l) + 1;
  }
};
struct CentroidPathDecomposition {
  struct Centroid {
    int ParIndex, ParDepth, Deep;
    vector<int> node;
    Centroid(int idx, int dep, int deep)
        : ParIndex(idx), ParDepth(dep), Deep(deep) {}
    inline size_t size() { return (node.size()); }
    inline int &operator[](int k) { return (node[k]); }
    inline pair<int, int> Up() { return (make_pair(ParIndex, ParDepth)); }
  };
  vector<vector<int>> graph;
  vector<int> SubTreeSize, NextPath;
  vector<int> TreeIndex, TreeDepth;
  vector<Centroid> Centroids;
  void BuildSubTreeSize() {
    stack<pair<int, int>> s;
    s.emplace(0, -1);
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      if (~SubTreeSize[p.first]) {
        NextPath[p.first] = -1;
        for (auto &to : graph[p.first]) {
          if (p.second == to)
            continue;
          SubTreeSize[p.first] += SubTreeSize[to];
          if (NextPath[p.first] == -1 ||
              SubTreeSize[NextPath[p.first]] < SubTreeSize[to]) {
            NextPath[p.first] = to;
          }
        }
      } else {
        s.push(p);
        SubTreeSize[p.first] = 1;
        for (auto &to : graph[p.first]) {
          if (p.second != to)
            s.emplace(to, p.first);
        }
      }
    }
  }
  void BuildPath() {
    stack<pair<int, int>> s;
    Centroids.emplace_back(-1, -1, 0);
    s.emplace(0, -1);
    TreeIndex[0] = 0;
    while (!s.empty()) {
      auto p = s.top();
      s.pop();
      TreeDepth[p.first] = (int)Centroids[TreeIndex[p.first]].size();
      for (auto &to : graph[p.first]) {
        if (p.second == to)
          continue;
        if (to == NextPath[p.first]) {
          TreeIndex[to] = TreeIndex[p.first];
        } else {
          TreeIndex[to] = (int)Centroids.size();
          Centroids.emplace_back(TreeIndex[p.first], TreeDepth[p.first],
                                 Centroids[TreeIndex[p.first]].Deep + 1);
        }
        s.emplace(to, p.first);
      }
      Centroids[TreeIndex[p.first]].node.emplace_back(p.first);
    }
  }
  void AddEdge(int x, int y) {
    graph[x].push_back(y);
    graph[y].push_back(x);
  }
  virtual void Build() {
    BuildSubTreeSize();
    BuildPath();
  }
  inline size_t size() { return (Centroids.size()); }
  inline pair<int, int> Information(int idx) {
    return (make_pair(TreeIndex[idx], TreeDepth[idx]));
  }
  inline Centroid &operator[](int k) { return (Centroids[k]); }
  inline int LCA(int a, int b) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    return (Centroids[TreeIdxA][TreeDepthA]);
  }
  inline virtual void query(int a, int b,
                            const function<void(int, int, int)> &f) {
    int TreeIdxA, TreeDepthA, TreeIdxB, TreeDepthB;
    tie(TreeIdxA, TreeDepthA) = Information(a);
    tie(TreeIdxB, TreeDepthB) = Information(b);
    while (TreeIdxA != TreeIdxB) {
      if (Centroids[TreeIdxA].Deep > Centroids[TreeIdxB].Deep) {
        f(TreeIdxA, 0, TreeDepthA + 1);
        tie(TreeIdxA, TreeDepthA) = Centroids[TreeIdxA].Up();
      } else {
        f(TreeIdxB, 0, TreeDepthB + 1);
        tie(TreeIdxB, TreeDepthB) = Centroids[TreeIdxB].Up();
      }
    }
    if (TreeDepthA > TreeDepthB)
      swap(TreeDepthA, TreeDepthB);
    f(TreeIdxA, TreeDepthA, TreeDepthB + 1);
  }
  CentroidPathDecomposition(int SZ) {
    graph.resize(SZ);
    SubTreeSize.assign(SZ, -1);
    NextPath.resize(SZ);
    TreeIndex.resize(SZ);
    TreeDepth.resize(SZ);
  }
};
OrderedMultiSet<int> tree(5000000);
OrderedMultiSet<int>::Node *nodes[100000];
int N, Q, X[100000];
vector<int> g[100000];
int parent[100000];
void dfs(int idx, int par, OrderedMultiSet<int>::Node *par_set) {
  parent[idx] = par;
  tree.insert_key(par_set, X[idx]);
  nodes[idx] = par_set;
  for (auto &to : g[idx])
    if (to != par)
      dfs(to, idx, par_set);
}
int main() {
  scanf("%d %d", &N, &Q);
  CentroidPathDecomposition gg(N);
  vector<int> vs;
  for (int i = 0; i < N; i++) {
    scanf("%d", &X[i]);
    vs.emplace_back(X[i]);
  }
  sort(begin(vs), end(vs));
  vs.erase(unique(begin(vs), end(vs)), end(vs));
  for (int i = 1; i < N; i++) {
    int x, y;
    scanf("%d %d", &x, &y);
    --x, --y;
    g[x].emplace_back(y);
    g[y].emplace_back(x);
    gg.AddEdge(x, y);
  }
  dfs(0, -1, tree.makeset());
  gg.Build();
  for (int i = 0; i < Q; i++) {
    int x, y, z;
    scanf("%d %d %d", &x, &y, &z);
    --x, --y;
    int lca = gg.LCA(x, y);
    int ng = -1, ok = (int)vs.size() - 1;
    auto sum = [&](int v) {
      int ret = 0;
      ret += tree.upper_bound(nodes[x], vs[v]);
      ret += tree.upper_bound(nodes[y], vs[v]);
      ret -= tree.upper_bound(nodes[lca], vs[v]);
      if (lca)
        ret -= tree.upper_bound(nodes[parent[lca]], vs[v]);
      return ret;
    };
    while (ok - ng > 1) {
      int mid = (ok + ng) >> 1;
      if (sum(mid) >= z)
        ok = mid;
      else
        ng = mid;
    }
    printf("%d\n", vs[ok]);
  }
}
accuracy: 1
time_ms: 1030
memory_kb: 127132
final_rank: 13