Problem:
農地開発
イヅア国では国内にある広大な土地を利用して、複数の農園を開拓することになった。
イヅア国ではすでに$N$箇所の農園が開拓されており、農園には$1$から$N$までの番号がついている。農園の間にはそれらを直接つなぐ道が、全部で$N-1$本ある。どの道も同じ長さで、どちらの方向にも通ることができる。また、どの農園からでも、いくつかの道を通っていくことで他のどの農園にも到達できる。また、それぞれの農園では、その農園の収支を記録している。
あなたは新しい任務としてイヅア国の農園の拡張と管理を任される予定である。上司から、以下の２種類の作業をいくつか組み合わせた指示を受け取ることになった。
作業１：農園を新たに開拓し、すでにある農園との間に道を作り接続する。新たに作る道の長さはすでにある道の長さと同じとする。
作業２：農園を２つ指定し、それらの農園をつなぐ最短の経路を見つける。その経路上にある農園の中から、$k$番目に収支の少ない農園の収支を報告する。
あなたは任務が始まる前に、報告すべき収支を計算するプログラムを作っておこうと考えている。
現在の農園の情報と、それぞれの作業に関する情報が与えられたとき、報告すべき農園の収支を出力するプログラムを作成せよ。
入力
入力は以下の形式で与えられる。
$N$ $M$
$b_1$
$b_2$
：
$b_N$
$s_1$ $t_1$
$s_2$ $t_2$
：
$s_{N-1}$ $t_{N-1}$
$op_1$
$op_2$
：
$op_M$
１行目に、すでにある農園の数$N$ ($2 \leq N \leq 100,000$)と作業の数$M$ ($1 \leq M \leq 100,000$)が与えられる。続く$N$行に、$i$番目の農園の収支$b_i$ ($-1,000,000 \leq b_i \leq 1,000,000$)が整数で与えられる。続く$N-1$行に、２つの農園を直接つなぐ道の情報が与えられる。$s_i$と$t_i$ ($1 \leq s_i < t_i \leq N$)は、$i$番目の道がつなぐ２つの農園の番号である。ただし、どの２つの農園についても、それらを直接つなぐ道は１本以下とする。続く$M$行に、作業を示す情報$op_i$が与えられる。各$op_i$は以下のいずれかの形式で与えられる。
1 $u$ $w$
または
2 $u$ $v$ $k$
以下では、作業を実行する前の農園の数を$P$とする。最初の文字が1の場合、番号が$P+1$となる農園を新たに作り、農園$u$ ($1 \leq u \leq P$)との間に道を作り接続する作業を示す。その収支は$w$ ($-1,000,000 \leq w \leq 1,000,000$)である。
最初の文字が2の場合、農園$u$ ($1 \leq u \leq P$)と農園$v$ ($1 \leq v \leq P$)を最短の道のりでたどったときに訪れるすべての農園の収支のうち、少ないほうから$k$ ($1 \leq k \leq P$)番目の収支を報告する作業を示す。ただし、$k$はこの指示で訪れる農園の数を超えない。
出力
収支を報告する作業ごとに、農園の収支を出力する。
入出力例
入力例
5 9
1
3
1
1
4
1 2
2 3
2 4
4 5
2 2 5 2
1 2 2
2 5 6 2
2 5 6 3
2 5 6 1
1 6 5
1 6 7
2 1 8 3
2 1 8 4
出力例
3
2
3
1
3
7

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (ll)(n); i++)
#define ll long long
const long long mod=998244353;
const long long hmod=46216567629137;
struct S{
    int pos;
    int d;
};
S e(){
    S res={-1,1000000000};
    return res;
}
S op(S a,S b){
    if(a.d<b.d) return a;
    return b;
}
class segtree{
    public:
    S dat[200009];
    int siz;
    segtree(int N){
        siz=1;
        while(siz<N) siz*=2;
        for(int i=1;i<siz*2;i++) dat[i]=e();
    }
    void set(int p,S x){
        p=p+siz-1;
        dat[p]=x;
        while(p>=2){
            p/=2;
            dat[p]=op(dat[p*2],dat[p*2+1]);
        }
    }
    S query(int l,int r,int a,int b,int u){
        if(r<=a || b<=l) return e();
        if(l<=a && b<=r) return dat[u];
        int m=(a+b)/2;
        S L=query(l,r,a,m,u*2);
        S R=query(l,r,m,b,u*2+1);
        return op(L,R);
    }
    S query(int l,int r){
        return query(l,r,1,siz+1,1);
    }
};
class BIT{
    public:
    int dat[2000009];
    vector<pair<int,int>>history[2000009];
    BIT(){
        for(int i=1;i<=2000001;i++){
            dat[i]=0;
            history[i].push_back({-1,0});
        }
    }
    void add(int p,int x,int t){
        while(p<=2000001){
            dat[p]+=x;
            history[p].push_back({t,dat[p]});
            p+=p&-p;
        }
    }
    int lower_bound(int a,int b,int c,int d,int k){
        int x=0;
        int s=1;
        while(s<2000001) s*=2;
        s/=2;
        for(int i=s;i>=1;i/=2){
            if(x+i>2000001) continue;
            int pos=x+i;
            int cnt=0;
            auto iter1=std::lower_bound(history[pos].begin(),history[pos].end(),make_pair(a+1,-100));
            auto iter2=std::lower_bound(history[pos].begin(),history[pos].end(),make_pair(b+1,-100));
            auto iter3=std::lower_bound(history[pos].begin(),history[pos].end(),make_pair(c+1,-100));
            iter1--;iter2--;iter3--;
            cnt+=(*iter1).second+(*iter2).second-(*iter3).second;
            if(d!=0){
                auto iter4=std::lower_bound(history[pos].begin(),history[pos].end(),make_pair(d+1,-100));
                iter4--;
                cnt-=(*iter4).second;
            }
            if(cnt<k){
                k-=cnt;
                x+=i;
            }
        }
        return x+1;
    }
};
int main(){
    cin.tie(0)->sync_with_stdio(0);
    cout.tie(0);
    //入力
    int N,M;
    cin>>N>>M;
    ll b[N+1];
    for(int i=1;i<=N;i++){
        cin>>b[i];
        b[i]+=1000001;
    }
    
    int s[N],t[N];
    for(int i=1;i<N;i++) cin>>s[i]>>t[i];
    int query[M+1],u[M+1],w[M+1],v[M+1],k[M+1];
    int nodes=N;
    for(int i=1;i<=M;i++){
        cin>>query[i];
        if(query[i]==1){
            cin>>u[i]>>w[i];
            w[i]+=1000001;
            nodes++;
        }
        if(query[i]==2) cin>>u[i]>>v[i]>>k[i];
    }
    //グラフ作成
    vector<int>G[nodes+1];
    for(int i=1;i<N;i++){
        G[s[i]].push_back(t[i]);
        G[t[i]].push_back(s[i]);
    }
    int c=N+1;
    for(int i=1;i<=M;i++){
        if(query[i]==1){
            G[u[i]].push_back(c);
            G[c].push_back(u[i]);
            c++;
        }
    }
    //costを調整
    int cost[nodes+1];
    for(int i=1;i<=N;i++){
        cost[i]=b[i];
    }
    c=N+1;
    for(int i=1;i<=M;i++){
        if(query[i]==1){
            cost[c]=w[i];
            c++;
        }
    }
    //オイラーツアーを作成+BITに要素を入れる
    BIT bit;
    vector<int>euler_tour;
    bool already[nodes+1];
    int T[nodes+1];
    int dist[nodes+1];
    for(int i=1;i<=nodes;i++){
        already[i]=0;
        T[i]=-1;
        dist[i]=0;
    }
    c=1;
    function<void(int)>dfs=[&](int pos){
        already[pos]=1;
        bit.add(cost[pos],1,c);
        c++;
        euler_tour.push_back(pos);
        for(int i=0;i<G[pos].size();i++){
            int nex=G[pos][i];
            if(already[nex]) continue;
            dist[nex]=dist[pos]+1;
            dfs(nex);
            euler_tour.push_back(pos);
        }
        already[pos]=0;
        bit.add(cost[pos],-1,c);
        c++;
        return;
    };
    dfs(1);
    segtree seg(euler_tour.size());
    for(int i=0;i<euler_tour.size();i++){
        int p=euler_tour[i];
        if(T[p]==-1) T[p]=i+1;
        seg.set(i+1,{p,dist[p]});
    }
    int T2[nodes+1];
    for(int i=1;i<=nodes;i++){
        T2[i]=-1;
        already[i]=0;
    }
    c=1;
    function<void(int)>dfs2=[&](int pos){
        already[pos]=1;
        if(T2[pos]==-1) T2[pos]=c;
        c++;
        for(int i=0;i<G[pos].size();i++){
            int nex=G[pos][i];
            if(already[nex]) continue;
            dfs2(nex);
        }
        already[pos]=0;
        c++;
        return;
    };
    dfs2(1);
    for(int i=1;i<=M;i++){
        if(query[i]==2){
            int a=T2[u[i]],b=T2[v[i]];
            if(a>b) swap(a,b);
            int c=seg.query(min(T[u[i]],T[v[i]]),max(T[u[i]],T[v[i]])+1).pos;
            c=T2[c];
            cout<<bit.lower_bound(a,b,c,c-1,k[i])-1000001<<"\n";
        }
    }
}
accuracy: 0.5714285714285714
time_ms: 290
memory_kb: 153340
final_rank: 18

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (ll)(n); i++)
#define ll long long
const long long mod=998244353;
const long long hmod=46216567629137;
struct S{
    int pos;
    int d;
};
S e(){
    S res={-1,1000000000};
    return res;
}
S op(S a,S b){
    if(a.d<b.d) return a;
    return b;
}
class segtree{
    public:
    S dat[200009];
    int siz;
    segtree(int N){
        siz=1;
        while(siz<N) siz*=2;
        for(int i=1;i<siz*2;i++) dat[i]=e();
    }
    void set(int p,S x){
        p=p+siz-1;
        dat[p]=x;
        while(p>=2){
            p/=2;
            dat[p]=op(dat[p*2],dat[p*2+1]);
        }
    }
    S query(int l,int r,int a,int b,int u){
        if(r<=a || b<=l) return e();
        if(l<=a && b<=r) return dat[u];
        int m=(a+b)/2;
        S L=query(l,r,a,m,u*2);
        S R=query(l,r,m,b,u*2+1);
        return op(L,R);
    }
    S query(int l,int r){
        return query(l,r,1,siz+1,1);
    }
};
class BIT{
    public:
    int dat[2000009];
    vector<pair<int,int>>history[2000009];
    BIT(){
        for(int i=1;i<=2000001;i++){
            dat[i]=0;
            history[i].push_back({-1,0});
        }
    }
    void add(int p,int x,int t){
        while(p<=2000000){
            dat[p]+=x;
            history[p].push_back({t,dat[p]});
            p+=p&-p;
        }
    }
    int lower_bound(int a,int b,int c,int d,int k){
        int x=0;
        int s=1;
        while(s<2000001) s*=2;
        s/=2;
        for(int i=s;i>=1;i/=2){
            if(x+i>2000001) continue;
            int pos=x+i;
            int cnt=0;
            auto iter1=std::lower_bound(history[pos].begin(),history[pos].end(),make_pair(a+1,-100));
            auto iter2=std::lower_bound(history[pos].begin(),history[pos].end(),make_pair(b+1,-100));
            auto iter3=std::lower_bound(history[pos].begin(),history[pos].end(),make_pair(c+1,-100));
            iter1--;iter2--;iter3--;
            cnt+=(*iter1).second+(*iter2).second-(*iter3).second;
            if(d!=0){
                auto iter4=std::lower_bound(history[pos].begin(),history[pos].end(),make_pair(d+1,-100));
                iter4--;
                cnt-=(*iter4).second;
            }
            if(cnt<k){
                k-=cnt;
                x+=i;
            }
        }
        return x+1;
    }
};
int main(){
    cin.tie(0)->sync_with_stdio(0);
    cout.tie(0);
    //入力
    int N,M;
    cin>>N>>M;
    ll b[N+1];
    for(int i=1;i<=N;i++){
        cin>>b[i];
        b[i]+=1000001;
    }
    
    int s[N],t[N];
    for(int i=1;i<N;i++) cin>>s[i]>>t[i];
    int query[M+1],u[M+1],w[M+1],v[M+1],k[M+1];
    int nodes=N;
    for(int i=1;i<=M;i++){
        cin>>query[i];
        if(query[i]==1){
            cin>>u[i]>>w[i];
            w[i]+=1000001;
            nodes++;
        }
        if(query[i]==2) cin>>u[i]>>v[i]>>k[i];
    }
    //グラフ作成
    vector<int>G[nodes+1];
    for(int i=1;i<N;i++){
        G[s[i]].push_back(t[i]);
        G[t[i]].push_back(s[i]);
    }
    int c=N+1;
    for(int i=1;i<=M;i++){
        if(query[i]==1){
            G[u[i]].push_back(c);
            G[c].push_back(u[i]);
            c++;
        }
    }
    //costを調整
    int cost[nodes+1];
    for(int i=1;i<=N;i++){
        cost[i]=b[i];
    }
    c=N+1;
    for(int i=1;i<=M;i++){
        if(query[i]==1){
            cost[c]=w[i];
            c++;
        }
    }
    //オイラーツアーを作成+BITに要素を入れる
    BIT bit;
    vector<int>euler_tour;
    bool already[nodes+1];
    int T[nodes+1];
    int dist[nodes+1];
    for(int i=1;i<=nodes;i++){
        already[i]=0;
        T[i]=-1;
        dist[i]=0;
    }
    c=1;
    function<void(int)>dfs=[&](int pos){
        already[pos]=1;
        bit.add(cost[pos],1,c);
        c++;
        euler_tour.push_back(pos);
        for(int i=0;i<G[pos].size();i++){
            int nex=G[pos][i];
            if(already[nex]) continue;
            dist[nex]=dist[pos]+1;
            dfs(nex);
            euler_tour.push_back(pos);
        }
        already[pos]=0;
        bit.add(cost[pos],-1,c);
        c++;
        return;
    };
    dfs(1);
    segtree seg(euler_tour.size());
    for(int i=0;i<euler_tour.size();i++){
        int p=euler_tour[i];
        if(T[p]==-1) T[p]=i+1;
        seg.set(i+1,{p,dist[p]});
    }
    int T2[nodes+1];
    for(int i=1;i<=nodes;i++){
        T2[i]=-1;
        already[i]=0;
    }
    c=1;
    function<void(int)>dfs2=[&](int pos){
        already[pos]=1;
        if(T2[pos]==-1) T2[pos]=c;
        c++;
        for(int i=0;i<G[pos].size();i++){
            int nex=G[pos][i];
            if(already[nex]) continue;
            dfs2(nex);
        }
        already[pos]=0;
        c++;
        return;
    };
    dfs2(1);
    for(int i=1;i<=M;i++){
        if(query[i]==2){
            int a=T2[u[i]],b=T2[v[i]];
            if(a>b) swap(a,b);
            int c=seg.query(min(T[u[i]],T[v[i]]),max(T[u[i]],T[v[i]])+1).pos;
            c=T2[c];
            cout<<bit.lower_bound(a,b,c,c-1,k[i])-1000001<<"\n";
        }
    }
}
accuracy: 0.21428571428571427
time_ms: 70
memory_kb: 122084
final_rank: 20

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (ll)(n); i++)
#define ll long long
const long long mod=998244353;
const long long hmod=46216567629137;
struct S{
    int pos;
    int d;
};
S e(){
    S res={-1,1000000000};
    return res;
}
S op(S a,S b){
    if(a.d<b.d) return a;
    return b;
}
class segtree{
    public:
    S dat[200009];
    int siz;
    segtree(int N){
        siz=1;
        while(siz<N) siz*=2;
        for(int i=1;i<siz*2;i++) dat[i]=e();
    }
    void set(int p,S x){
        p=p+siz-1;
        dat[p]=x;
        while(p>=2){
            p/=2;
            dat[p]=op(dat[p*2],dat[p*2+1]);
        }
    }
    S query(int l,int r,int a,int b,int u){
        if(r<=a || b<=l) return e();
        if(l<=a && b<=r) return dat[u];
        int m=(a+b)/2;
        S L=query(l,r,a,m,u*2);
        S R=query(l,r,m,b,u*2+1);
        return op(L,R);
    }
    S query(int l,int r){
        return query(l,r,1,siz+1,1);
    }
};
class BIT{
    public:
    int dat[2000009];
    vector<pair<int,int>>history[2000009];
    BIT(){
        for(int i=1;i<=2000000;i++){
            dat[i]=0;
            history[i].push_back({-1,0});
        }
    }
    void add(int p,int x,int t){
        while(p<=2000000){
            dat[p]+=x;
            history[p].push_back({t,dat[p]});
            p+=p&-p;
        }
    }
    int lower_bound(int a,int b,int c,int d,int k){
        int x=0;
        int s=1;
        while(s<2000000) s*=2;
        s/=2;
        for(int i=s;i>=1;i/=2){
            if(x+i>2000000) continue;
            int pos=x+i;
            int cnt=0;
            auto iter1=std::lower_bound(history[pos].begin(),history[pos].end(),make_pair(a+1,-100));
            auto iter2=std::lower_bound(history[pos].begin(),history[pos].end(),make_pair(b+1,-100));
            auto iter3=std::lower_bound(history[pos].begin(),history[pos].end(),make_pair(c+1,-100));
            iter1--;iter2--;iter3--;
            cnt+=(*iter1).second+(*iter2).second-(*iter3).second;
            if(d!=0){
                auto iter4=std::lower_bound(history[pos].begin(),history[pos].end(),make_pair(d+1,-100));
                iter4--;
                cnt-=(*iter4).second;
            }
            if(cnt<k){
                k-=cnt;
                x+=i;
            }
        }
        return x+1;
    }
};
int main(){
    cin.tie(0)->sync_with_stdio(0);
    cout.tie(0);
    //入力
    int N,M;
    cin>>N>>M;
    ll b[N+1];
    for(int i=1;i<=N;i++){
        cin>>b[i];
        b[i]+=1000001;
    }
    
    int s[N],t[N];
    for(int i=1;i<N;i++) cin>>s[i]>>t[i];
    int query[M+1],u[M+1],w[M+1],v[M+1],k[M+1];
    int nodes=N;
    for(int i=1;i<=M;i++){
        cin>>query[i];
        if(query[i]==1){
            cin>>u[i]>>w[i];
            w[i]+=1000001;
            nodes++;
        }
        if(query[i]==2) cin>>u[i]>>v[i]>>k[i];
    }
    //グラフ作成
    vector<int>G[nodes+1];
    for(int i=1;i<N;i++){
        G[s[i]].push_back(t[i]);
        G[t[i]].push_back(s[i]);
    }
    int c=N+1;
    for(int i=1;i<=M;i++){
        if(query[i]==1){
            G[u[i]].push_back(c);
            G[c].push_back(u[i]);
            c++;
        }
    }
    //costを調整
    int cost[nodes+1];
    for(int i=1;i<=N;i++){
        cost[i]=b[i];
    }
    c=N+1;
    for(int i=1;i<=M;i++){
        if(query[i]==1){
            cost[c]=w[i];
            c++;
        }
    }
    //オイラーツアーを作成+BITに要素を入れる
    BIT bit;
    vector<int>euler_tour;
    bool already[nodes+1];
    int T[nodes+1];
    int dist[nodes+1];
    for(int i=1;i<=nodes;i++){
        already[i]=0;
        T[i]=-1;
        dist[i]=0;
    }
    c=1;
    function<void(int)>dfs=[&](int pos){
        already[pos]=1;
        bit.add(cost[pos],1,c);
        c++;
        euler_tour.push_back(pos);
        for(int i=0;i<G[pos].size();i++){
            int nex=G[pos][i];
            if(already[nex]) continue;
            dist[nex]=dist[pos]+1;
            dfs(nex);
            euler_tour.push_back(pos);
        }
        already[pos]=0;
        bit.add(cost[pos],-1,c);
        c++;
        return;
    };
    dfs(1);
    segtree seg(euler_tour.size());
    for(int i=0;i<euler_tour.size();i++){
        int p=euler_tour[i];
        if(T[p]==-1) T[p]=i+1;
        seg.set(i+1,{p,dist[p]});
    }
    int T2[nodes+1];
    for(int i=1;i<=nodes;i++){
        T2[i]=-1;
        already[i]=0;
    }
    c=1;
    function<void(int)>dfs2=[&](int pos){
        already[pos]=1;
        if(T2[pos]==-1) T2[pos]=c;
        c++;
        for(int i=0;i<G[pos].size();i++){
            int nex=G[pos][i];
            if(already[nex]) continue;
            dfs2(nex);
        }
        already[pos]=0;
        c++;
        return;
    };
    dfs2(1);
    for(int i=1;i<=M;i++){
        if(query[i]==2){
            int a=T2[u[i]],b=T2[v[i]];
            if(a>b) swap(a,b);
            int c=seg.query(min(T[u[i]],T[v[i]]),max(T[u[i]],T[v[i]])+1).pos;
            c=T2[c];
            cout<<bit.lower_bound(a,b,c,c-1,k[i])-1000001<<"\n";
        }
    }
}
accuracy: 0.5714285714285714
time_ms: 290
memory_kb: 153464
final_rank: 19

code_snippet: #include <bits/stdc++.h>
using namespace std;
// #include<atcoder/all>
// using mint = atcoder::modint998244353;
using ld = long double;
#define fi first
#define se second
#define all(x) x.begin(),x.end()
#define rep(i,n) for(int i=0;i<(int)(n);++i)
template<typename T>bool chmin(T&a,T b){return b<a?(a=b,1):0;}
template<typename T>bool chmax(T&a,T b){return b>a?(a=b,1):0;}

struct LCA{
    int N;
    vector<vector<int>> G,parent;
    vector<int> depth;
    LCA(const vector<vector<int>> &G_, int root=0):N(G_.size()), G(G_), depth(G_.size())
    {
        int lg = 0;
        while((1<<lg)+1<N)lg++;
        parent.assign(lg+1, vector<int>(N));

        dfs(root,-1,0);
        for(int k=1;k<(int)parent.size();k++)
        {
            for(int i=0;i<N;i++)
            {
                if(parent[k-1][i]==-1)parent[k][i]=-1;
                else parent[k][i]=parent[k-1][parent[k-1][i]];
            }
        }

    }
    void dfs(int u, int p, int d)
    {
        depth[u]=d;
        parent[0][u]=p;
        for(int v:G[u]) if(v!=p) dfs(v, u, d+1);
    }
    int lca(int u, int v)
    {
        if(depth[u]>depth[v])swap(u,v);
        for(int k=0;k<(int)parent.size();k++) if((depth[v]-depth[u])>>k&1) v=parent[k][v];
        if(u==v) return u;
        for(int k=parent.size();k--;) if(parent[k][u]!=parent[k][v])
        {
            u=parent[k][u];
            v=parent[k][v];
        }
        return parent[0][u];
    }
};


template <class T> struct fenwick_tree {
    using U = T;

  public:
    fenwick_tree() : _n(0) {}
    explicit fenwick_tree(int n) : _n(n), data(n) {}

    void add(int p, T x) {
        assert(0 <= p && p < _n);
        p++;
        while (p <= _n) {
            data[p - 1] += U(x);
            p += p & -p;
        }
    }

    T sum(int l, int r) {
        assert(0 <= l && l <= r && r <= _n);
        return sum(r) - sum(l);
    }

  private:
    int _n;
    std::vector<U> data;

    U sum(int r) {
        U s = 0;
        while (r > 0) {
            s += data[r - 1];
            r -= r & -r;
        }
        return s;
    }
};

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int N,M;
    cin>>N>>M;

    vector<int> B(N);
    rep(i,N)cin>>B[i], B[i]+=1000000;
    vector<vector<int>> G(N);
    rep(i,N-1){
        int a,b;
        cin>>a>>b;
        a--;b--;
        G[a].push_back(b);
        G[b].push_back(a);
    }

    vector<array<int,3>> ql;
    while(M--){
        int o;
        cin>>o;
        if(o==1){
            int u,w;
            cin>>u>>w;
            u--;
            w+=1000000;
            G[u].push_back(ssize(G));
            G.push_back({u});
            B.push_back(w);
        }
        else{
            int u,v,k;
            cin>>u>>v>>k;
            u--;v--;
            ql.push_back({u,v,k});
        }
    }
    N=ssize(G);
    int L=ssize(ql);
    
    
    LCA lca(G);
    
    vector<vector<pair<int,int>>> T(N);
    rep(i,L){
        T[ql[i][0]].push_back({i, 1});
        T[ql[i][1]].push_back({i, 1});
        T[lca.lca(ql[i][0], ql[i][1])].push_back({i, -1});
    }

    vector<int> ok(L,2000000),ng(L,-1);
    rep(_,22){
        fenwick_tree<int> bit(2000001);
        vector<int> m(L);
        rep(i,L) m[i]=(ok[i]+ng[i])/2;

        vector<int> cnt(L,0);
        auto dfs=[&](auto dfs, int v, int p)->void {
            for(auto [qi, s]: T[v]){
                if(s==-1)cnt[qi]+=bit.sum(0, m[qi]+1)*s;
            }
            bit.add(B[v], 1);
            for(auto [qi, s]: T[v]){
                cnt[qi]+=bit.sum(0, m[qi]+1)*s;
            }
            for(int e: G[v]){
                if(e==p) continue;
                dfs(dfs, e, v);
            }
            bit.add(B[v], -1);
        };

        dfs(dfs, 0, -1);

        rep(i,L){
            if(cnt[i]>=ql[i][2]){
                ok[i]=m[i];
            }
            else{
                ng[i]=m[i];
            }
        }
    }
    rep(i,L)cout<<ok[i]-1000000<<"\n";


}
accuracy: 1
time_ms: 320
memory_kb: 50940
final_rank: 9

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define int long long
struct SEG{
private:
    int n;
    vector<int> node;
public:
    SEG(int N){
        n = 1;
        while(n < N) n *= 2;
        node.resize(2*n-1);
    }
    void add(int i, int x){
        i += n-1;
        node[i] += x;
        while(i){
            i = (i-1)/2;
            node[i] = node[2*i+1] + node[2*i+2];
        }
    }
    int f(int a, int b, int k = 0, int l = 0, int r = -1){
        if(r < 0) r = n;
        if(a <= l && r <= b) return node[k];
        if(b <= l || r <= a) return 0;
        return (f(a,b,2*k+1,l,(l+r)/2) + f(a,b,2*k+2,(l+r)/2,r));
    }
};
signed main(){
    int N,Q;
    cin>>N>>Q;
    vector<pair<int,int>> S(N);
    vector<tuple<int,int,int,int>> TT = {};
    for(int i=0;i<N;i++){
        cin>>S[i].first;
        S[i].second = i;
    }
    vector<vector<int>> G(N);
    for(int i=0;i<N-1;i++){
        int a,b;
        cin>>a>>b;
        a--;
        b--;
        G[a].push_back(b);
        G[b].push_back(a);
    }
    int ni = 0;
    while(Q--){
        int t;
        cin>>t;
        if(t == 1){
            int a,b;
            cin>>a>>b;
            a--;
            int t = G.size();
            vector<int> z = {};
            G.push_back(z);
            G[t].push_back(a);
            G[a].push_back(t);
            S.push_back({b,t});
        }
        else{
            int a,b,c;
            cin>>a>>b>>c;
            a--;
            b--;
            TT.push_back({a,b,c,ni});
            ni++;
        }
    }
    N = G.size();
    vector<vector<tuple<int,int,int,int,int,int>>> T(N); // uvklri
    for(auto [a,b,c,i]:TT) T[N/2].push_back({a,b,c,0,N,i});
    int n = 18;
    vector<int> in(N), out(N);
    vector<vector<int>> P(n,vector<int>(N,-1));
    vector<int> dist(N);
    int nowd = 0;
    auto f = [&](auto &&self, int a, int p) -> void {
        P[0][a] = p;
        in[a] = nowd;
        nowd++;
        for(int x:G[a])if(x != p){
            dist[x] = dist[a] + 1;
            self(self,x,a);
        }
        out[a] = nowd;
        nowd++;
    };
    f(f,0,-1);
    for(int i=1;i<n;i++)for(int j=0;j<N;j++)if(P[i-1][j] != -1) P[i][j] = P[i-1][P[i-1][j]];
    auto lca = [&](int a, int b) -> int{
        if(dist[a] > dist[b]) swap(a,b);
        int t = dist[b]-dist[a];
        for(int i=0;i<n;i++)if(t&(1<<i)) b = P[i][b];
        if(a == b) return a;
        for(int i=n-1;i>=0;i--)if(P[i][a] != -1 && P[i][a] != P[i][b]){
            a = P[i][a];
            b = P[i][b];
        }
        return P[0][a];
    };
    sort(S.begin(),S.end());
    int an = 0;
    for(int z=0;z<n;z++){
        vector<vector<tuple<int,int,int,int,int,int>>> nT(N);
        SEG seg(2*N+10);
        for(int md=0;md<N;md++){
            for(auto [u,v,k,l,r,i]:T[md]){
                if(!z) an++;
                int p = lca(u,v);
                if(seg.f(0,in[u]+1)+seg.f(0,in[v]+1)-2*seg.f(0,in[p]+1)+seg.f(in[p],in[p]+1) >= k) nT[(l+md)/2].push_back({u,v,k,l,md,i});
                else nT[(md+r)/2].push_back({u,v,k,md,r,i});
            }
            seg.add(in[S[md].second],1);
            seg.add(out[S[md].second],-1);
        }
        T = nT;
    }
    vector<int> Ans(an);
    for(int i=0;i<N;i++)for(auto [u,v,k,l,r,j]:T[i]){
        Ans[j] = S[i].first;
    }
    for(int x:Ans) cout<<x<<endl;
}
accuracy: 1
time_ms: 1260
memory_kb: 129756
final_rank: 15

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

#ifdef LOCAL
#include <debug.hpp>
#else
#define debug(...)
#endif

struct LowestCommonAncestorDoubling {
    vector<vector<int>> dp;  // dp[k][v] := 頂点 v の 2^k 先の親の頂点
    vector<int> depth;       // depth[v] := 頂点 v の root からの深さ
    int LOG;

    LowestCommonAncestorDoubling(const vector<vector<int>>& G, int root = 0) { build(G, root); }
    void build(const vector<vector<int>>& G, int root) {
        int V = G.size();
        LOG = 1;
        while ((1 << LOG) < V) LOG++;
        dp.assign(LOG, vector<int>(V, -1));
        depth.assign(V, -1);
        dfs(G, root, -1, 0);  // 2^0 先の親は dfs で求める
        for (int k = 0; k < LOG - 1; k++) {
            for (int i = 0; i < V; i++) {
                if (dp[k][i] == -1) {
                    dp[k + 1][i] = dp[k][i];
                } else {
                    dp[k + 1][i] = dp[k][dp[k][i]];
                }
            }
        }
    }
    void dfs(const vector<vector<int>>& G, int pos, int parent, int dist) {
        dp[0][pos] = parent;
        depth[pos] = dist;
        for (auto to : G[pos]) {
            if (to == parent) continue;
            dfs(G, to, pos, dist + 1);
        }
    }
    // 頂点 pos の d 個先の親
    int level_ancestor(int pos, int d) {
        if (depth[pos] < d) return -1;
        for (int i = 0; i < LOG; i++) {
            if ((d >> i) & 1) {
                pos = dp[i][pos];
            }
        }
        return pos;
    }
    // u, v の LCA を求める
    int lca(int u, int v) {
        if (depth[u] < depth[v]) swap(u, v);         // 頂点 u を深い方にする
        u = level_ancestor(u, depth[u] - depth[v]);  // u と v の高さを揃える
        if (u == v) return u;
        // ダブリング上の二分探索
        for (int i = LOG - 1; i >= 0; i--) {
            if (dp[i][u] != dp[i][v]) {
                u = dp[i][u];
                v = dp[i][v];
            }
        }
        return dp[0][u];
    }
    // u - v パスの距離
    int dist(int u, int v) { return depth[u] + depth[v] - 2 * depth[lca(u, v)]; }
    // u - v パス上に x が存在するか
    bool is_on_path(int u, int v, int x) { return dist(u, x) + dist(x, v) == dist(u, v); }
    // u - v パス上の頂点のうち, u からの距離が d の頂点
    int jump(int u, int v, int d) {
        int l = lca(u, v);
        int d1 = depth[u] - depth[l];
        // u - l パス上に x が存在する場合
        if (d <= d1) return level_ancestor(u, d);
        int d2 = d1 + depth[v] - depth[l];
        // l - v パス上に x が存在する場合
        if (d <= d2) return level_ancestor(v, d - d1);
        return -1;
    }
};

template <class T>
struct BinaryIndexedTree {
    int N;
    vector<T> data;

    BinaryIndexedTree() = default;
    BinaryIndexedTree(int size) : N(size), data(size + 1, 0) {}
    BinaryIndexedTree(const vector<T>& v) : N(ssize(v)), data(N + 1) {
        for (int i = 1; i <= N; i++) {
            data[i] += v[i - 1];
            int j = i + (i & -i);
            if (j <= N) data[j] += data[i];
        }
    }
    // i 番目に x を加算
    void add(int i, T x) {
        assert(0 <= i && i < N);
        for (++i; i <= N; i += i & -i) data[i] += x;
    }
    // [l, r) に x を加算 (imos 法)
    void imos(int l, int r, T x) { add(l, x), add(r, -x); }
    // [0, i) の和 (imos 法で区間加算して i 番目の値を取るときは sum(i + 1))
    T sum(int i) const {
        if (i < 0) return T{};
        T res{};
        for (; i > 0; i -= i & -i) res += data[i];
        return res;
    }
    // [l, r) の和
    T sum(int l, int r) const { return sum(r) - sum(l); }
    // i 番目の値
    T operator[](int i) const { return sum(i + 1) - sum(i); }
    // [0, i) の和が w 以上となる最小の i
    int lower_bound(T w) const {
        if (w <= 0) return 0;
        int i = 0;
        for (int k = 1 << __lg(N); k > 0; k >>= 1) {
            if (i + k <= N && data[i + k] < w) {
                w -= data[i + k];
                i += k;
            }
        }
        return i + 1;
    }
};

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);
    int N, M;
    cin >> N >> M;
    // 負の値を無くすために offset を加える
    constexpr int offset = 1000000;
    vector<int> B(N);
    for (int i = 0; i < N; i++) cin >> B[i], B[i] += offset;
    // あらかじめクエリを先読みし、最初の文字が 1 のクエリを処理した後の木 G を構築
    vector<vector<int>> G(N + M);
    for (int i = 0; i < N - 1; i++) {
        int u, v;
        cin >> u >> v, u--, v--;
        G[u].emplace_back(v);
        G[v].emplace_back(u);
    }
    // 最初の文字が 2 のクエリの {u, v, k} を格納
    vector<tuple<int, int, int>> query;
    for (int i = 0; i < M; i++) {
        int t;
        cin >> t;
        if (t == 1) {
            int u, w;
            cin >> u >> w, u--, w += offset;
            G[N].emplace_back(u);
            G[u].emplace_back(N);
            B.emplace_back(w);
            N++;
        }
        if (t == 2) {
            int u, v, k;
            cin >> u >> v >> k, u--, v--;
            query.emplace_back(u, v, k);
        }
    }
    M = query.size();

    LowestCommonAncestorDoubling lca(G);

    // check[i] := 頂点 i がどのクエリを処理するときに使用するか
    // 1 の場合は加算, 0 の場合は減算
    vector<vector<pair<int, int>>> check(N);
    for (int i = 0; i < M; i++) {
        auto [u, v, k] = query[i];
        check[u].emplace_back(i, 1);  // out
        check[v].emplace_back(i, 1);  // out
        int L = lca.lca(u, v);
        if (L != 0) {
            check[lca.level_ancestor(L, 1)].emplace_back(i, 0);  // in
        }
        check[L].emplace_back(i, 0);  // in
    }

    // 並列二分探索
    // 判定問題: クエリ i の u - v パス内の mid 以下の頂点数が k 以上か
    // 最小値が答え
    vector<int> ok(M, 2 * offset + 1), ng(M, -1);
    while (true) {
        // is_end := 全てのクエリ i
        bool is_end = true;
        for (int i = 0; i < M; i++) {
            if (ok[i] - ng[i] > 1) is_end = false;
        }
        if (is_end) break;
        vector<int> mid(M);
        for (int i = 0; i < M; i++) mid[i] = midpoint(ok[i], ng[i]);

        // num[i] := クエリ i の u - v パス内の mid[i] 以下の頂点数
        vector<int> num(M);
        BinaryIndexedTree<int> bit(2 * offset + 1);
        auto dfs = [&](auto&& self, int v, int p) -> void {
            bit.add(B[v], 1);
            for (auto [i, type] : check[v]) {
                if (type == 0) {
                    // in
                    // mid[i] 以下の頂点を引く
                    num[i] -= bit.sum(mid[i] + 1);
                }
                if (type == 1) {
                    // out
                    // mid[i] 以下の頂点を足す
                    num[i] += bit.sum(mid[i] + 1);
                }
            }
            for (auto to : G[v]) {
                if (to == p) continue;
                self(self, to, v);
            }
            bit.add(B[v], -1);
        };
        dfs(dfs, 0, -1);
        for (int i = 0; i < M; i++) {
            auto [u, v, k] = query[i];
            (num[i] >= k ? ok[i] : ng[i]) = mid[i];
        }
    }

    for (int i = 0; i < M; i++) {
        cout << ok[i] - offset << '\n';
    }
}
accuracy: 1
time_ms: 290
memory_kb: 49564
final_rank: 8

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

#ifdef LOCAL
#include <debug.hpp>
#else
#define debug(...)
#endif

struct LowestCommonAncestorDoubling {
    vector<vector<int>> dp;  // dp[k][v] := 頂点 v の 2^k 先の親の頂点
    vector<int> depth;       // depth[v] := 頂点 v の root からの深さ
    int LOG;

    LowestCommonAncestorDoubling(const vector<vector<int>>& G, int root = 0) { build(G, root); }
    void build(const vector<vector<int>>& G, int root) {
        int V = G.size();
        LOG = 1;
        while ((1 << LOG) < V) LOG++;
        dp.assign(LOG, vector<int>(V, -1));
        depth.assign(V, -1);
        dfs(G, root, -1, 0);  // 2^0 先の親は dfs で求める
        for (int k = 0; k < LOG - 1; k++) {
            for (int i = 0; i < V; i++) {
                if (dp[k][i] == -1) {
                    dp[k + 1][i] = dp[k][i];
                } else {
                    dp[k + 1][i] = dp[k][dp[k][i]];
                }
            }
        }
    }
    void dfs(const vector<vector<int>>& G, int pos, int parent, int dist) {
        dp[0][pos] = parent;
        depth[pos] = dist;
        for (auto to : G[pos]) {
            if (to == parent) continue;
            dfs(G, to, pos, dist + 1);
        }
    }
    // 頂点 pos の d 個先の親
    int level_ancestor(int pos, int d) {
        if (depth[pos] < d) return -1;
        for (int i = 0; i < LOG; i++) {
            if ((d >> i) & 1) {
                pos = dp[i][pos];
            }
        }
        return pos;
    }
    // u, v の LCA を求める
    int lca(int u, int v) {
        if (depth[u] < depth[v]) swap(u, v);         // 頂点 u を深い方にする
        u = level_ancestor(u, depth[u] - depth[v]);  // u と v の高さを揃える
        if (u == v) return u;
        // ダブリング上の二分探索
        for (int i = LOG - 1; i >= 0; i--) {
            if (dp[i][u] != dp[i][v]) {
                u = dp[i][u];
                v = dp[i][v];
            }
        }
        return dp[0][u];
    }
    // u - v パスの距離
    int dist(int u, int v) { return depth[u] + depth[v] - 2 * depth[lca(u, v)]; }
    // u - v パス上に x が存在するか
    bool is_on_path(int u, int v, int x) { return dist(u, x) + dist(x, v) == dist(u, v); }
    // u - v パス上の頂点のうち, u からの距離が d の頂点
    int jump(int u, int v, int d) {
        int l = lca(u, v);
        int d1 = depth[u] - depth[l];
        // u - l パス上に x が存在する場合
        if (d <= d1) return level_ancestor(u, d);
        int d2 = d1 + depth[v] - depth[l];
        // l - v パス上に x が存在する場合
        if (d <= d2) return level_ancestor(v, d - d1);
        return -1;
    }
};

// ref: https://nyaannyaan.github.io/library/data-structure/hash-map-variable-length.hpp
template <typename Key, typename Val>
struct HashMap {
    using u32 = uint32_t;
    using u64 = uint64_t;

    u32 cap, s;
    vector<Key> keys;
    vector<Val> vals;
    vector<bool> flag;
    u64 r;
    u32 shift;
    Val DefaultValue;

    static u64 rng() {
        u64 m = chrono::duration_cast<chrono::nanoseconds>(chrono::high_resolution_clock::now().time_since_epoch()).count();
        m ^= m >> 16;
        m ^= m << 32;
        return m;
    }

    void reallocate() {
        cap <<= 1;
        vector<Key> k(cap);
        vector<Val> v(cap);
        vector<bool> f(cap);
        u32 sh = shift - 1;
        for (int i = 0; i < (int)flag.size(); i++) {
            if (flag[i]) {
                u32 hash = (u64(keys[i]) * r) >> sh;
                while (f[hash]) hash = (hash + 1) & (cap - 1);
                k[hash] = keys[i];
                v[hash] = vals[i];
                f[hash] = 1;
            }
        }
        keys.swap(k);
        vals.swap(v);
        flag.swap(f);
        --shift;
    }

    explicit HashMap() : cap(8), s(0), keys(cap), vals(cap), flag(cap), r(rng()), shift(64 - __lg(cap)), DefaultValue(Val()) {}

    Val& operator[](const Key& i) {
        u32 hash = (u64(i) * r) >> shift;
        while (true) {
            if (!flag[hash]) {
                if (s + s / 4 >= cap) {
                    reallocate();
                    return (*this)[i];
                }
                keys[hash] = i;
                flag[hash] = 1;
                ++s;
                return vals[hash] = DefaultValue;
            }
            if (keys[hash] == i) return vals[hash];
            hash = (hash + 1) & (cap - 1);
        }
    }

    // exist -> return pointer of Val
    // not exist -> return nullptr
    const Val* find(const Key& i) const {
        u32 hash = (u64(i) * r) >> shift;
        while (true) {
            if (!flag[hash]) return nullptr;
            if (keys[hash] == i) return &(vals[hash]);
            hash = (hash + 1) & (cap - 1);
        }
    }

    // return vector< pair<const Key&, val& > >
    vector<pair<Key, Val>> enumerate() const {
        vector<pair<Key, Val>> ret;
        for (u32 i = 0; i < cap; ++i)
            if (flag[i]) ret.emplace_back(keys[i], vals[i]);
        return ret;
    }

    int size() const { return s; }

    // set default_value
    void set_default(const Val& val) { DefaultValue = val; }
};

/**
 * @brief Hash Map(可変長版)
 * @docs docs/data-structure/hash-map.md
 */

template <class S, class T>
struct DynamicBinaryIndexedTree {
    S N;
    HashMap<S, T> data;
    DynamicBinaryIndexedTree() = default;
    DynamicBinaryIndexedTree(S size) : N(size + 1) {}

    void add(S i, T x) {
        assert(0 <= i && i < N);
        for (++i; i <= N; i += i & -i) data[i] += x;
    }
    void imos(int l, int r, T x) { add(l, x), add(r, -x); }
    T sum(S i) const {
        if (i <= 0) return T{};
        T res{};
        for (; i > 0; i -= i & -i) {
            // std::unordered_map の場合
            // auto p = data.find(i);
            // res += p != data.end() ? p->second : T{};
            auto p = data.find(i);
            res += p ? *p : T{};
        }
        return res;
    }
    T sum(S l, S r) const { return sum(r) - sum(l); }
    T operator[](S i) const { return sum(i + 1) - sum(i); }
    S lower_bound(T w) {
        if (w <= 0) return 0;
        S i = 0;
        for (S k = S(1) << __lg(N); k > 0; k >>= 1) {
            if (i + k <= N && data[i + k] < w) {
                w -= data[i + k];
                i += k;
            }
        }
        return i + 1;
    }
};

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);
    int N, M;
    cin >> N >> M;
    // 負の値を無くすために offset を加える
    constexpr int offset = 1000000;
    vector<int> B(N);
    for (int i = 0; i < N; i++) cin >> B[i], B[i] += offset;
    // あらかじめクエリを先読みし、最初の文字が 1 のクエリを処理した後の木 G を構築
    vector<vector<int>> G(N + M);
    for (int i = 0; i < N - 1; i++) {
        int u, v;
        cin >> u >> v, u--, v--;
        G[u].emplace_back(v);
        G[v].emplace_back(u);
    }
    // 最初の文字が 2 のクエリの {u, v, k} を格納
    vector<tuple<int, int, int>> query;
    for (int i = 0; i < M; i++) {
        int t;
        cin >> t;
        if (t == 1) {
            int u, w;
            cin >> u >> w, u--, w += offset;
            G[N].emplace_back(u);
            G[u].emplace_back(N);
            B.emplace_back(w);
            N++;
        }
        if (t == 2) {
            int u, v, k;
            cin >> u >> v >> k, u--, v--;
            query.emplace_back(u, v, k);
        }
    }
    M = query.size();

    LowestCommonAncestorDoubling lca(G);

    // check[i] := 頂点 i がどのクエリを処理するときに使用するか
    // 1 の場合は加算, 0 の場合は減算
    vector<vector<pair<int, int>>> check(N);
    for (int i = 0; i < M; i++) {
        auto [u, v, k] = query[i];
        check[u].emplace_back(i, 1);  // out
        check[v].emplace_back(i, 1);  // out
        int L = lca.lca(u, v);
        if (L != 0) {
            check[lca.level_ancestor(L, 1)].emplace_back(i, 0);  // in
        }
        check[L].emplace_back(i, 0);  // in
    }

    // 並列二分探索
    // 判定問題: クエリ i の u - v パス内の mid 以下の頂点数が k 以上か
    // 最小値が答え
    vector<int> ok(M, 2 * offset + 1), ng(M, -1);
    while (true) {
        // is_end := 全てのクエリ i
        bool is_end = true;
        for (int i = 0; i < M; i++) {
            if (ok[i] - ng[i] > 1) is_end = false;
        }
        if (is_end) break;
        vector<int> mid(M);
        for (int i = 0; i < M; i++) mid[i] = midpoint(ok[i], ng[i]);

        // num[i] := クエリ i の u - v パス内の mid[i] 以下の頂点数
        vector<int> num(M);
        DynamicBinaryIndexedTree<int, int> bit(2 * offset + 1);
        auto dfs = [&](auto&& self, int v, int p) -> void {
            bit.add(B[v], 1);
            for (auto [i, type] : check[v]) {
                if (type == 0) {
                    // in
                    // mid[i] 以下の頂点を引く
                    num[i] -= bit.sum(mid[i] + 1);
                }
                if (type == 1) {
                    // out
                    // mid[i] 以下の頂点を足す
                    num[i] += bit.sum(mid[i] + 1);
                }
            }
            for (auto to : G[v]) {
                if (to == p) continue;
                self(self, to, v);
            }
            bit.add(B[v], -1);
        };
        dfs(dfs, 0, -1);
        for (int i = 0; i < M; i++) {
            auto [u, v, k] = query[i];
            (num[i] >= k ? ok[i] : ng[i]) = mid[i];
        }
    }

    for (int i = 0; i < M; i++) {
        cout << ok[i] - offset << '\n';
    }
}
accuracy: 1
time_ms: 860
memory_kb: 45200
final_rank: 12

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

#ifdef LOCAL
#include <debug.hpp>
#else
#define debug(...)
#endif

struct LowestCommonAncestorDoubling {
    vector<vector<int>> dp;  // dp[k][v] := 頂点 v の 2^k 先の親の頂点
    vector<int> depth;       // depth[v] := 頂点 v の root からの深さ
    int LOG;

    LowestCommonAncestorDoubling(const vector<vector<int>>& G, int root = 0) { build(G, root); }
    void build(const vector<vector<int>>& G, int root) {
        int V = G.size();
        LOG = 1;
        while ((1 << LOG) < V) LOG++;
        dp.assign(LOG, vector<int>(V, -1));
        depth.assign(V, -1);
        dfs(G, root, -1, 0);  // 2^0 先の親は dfs で求める
        for (int k = 0; k < LOG - 1; k++) {
            for (int i = 0; i < V; i++) {
                if (dp[k][i] == -1) {
                    dp[k + 1][i] = dp[k][i];
                } else {
                    dp[k + 1][i] = dp[k][dp[k][i]];
                }
            }
        }
    }
    void dfs(const vector<vector<int>>& G, int pos, int parent, int dist) {
        dp[0][pos] = parent;
        depth[pos] = dist;
        for (auto to : G[pos]) {
            if (to == parent) continue;
            dfs(G, to, pos, dist + 1);
        }
    }
    // 頂点 pos の d 個先の親
    int level_ancestor(int pos, int d) {
        if (depth[pos] < d) return -1;
        for (int i = 0; i < LOG; i++) {
            if ((d >> i) & 1) {
                pos = dp[i][pos];
            }
        }
        return pos;
    }
    // u, v の LCA を求める
    int lca(int u, int v) {
        if (depth[u] < depth[v]) swap(u, v);         // 頂点 u を深い方にする
        u = level_ancestor(u, depth[u] - depth[v]);  // u と v の高さを揃える
        if (u == v) return u;
        // ダブリング上の二分探索
        for (int i = LOG - 1; i >= 0; i--) {
            if (dp[i][u] != dp[i][v]) {
                u = dp[i][u];
                v = dp[i][v];
            }
        }
        return dp[0][u];
    }
    // u - v パスの距離
    int dist(int u, int v) { return depth[u] + depth[v] - 2 * depth[lca(u, v)]; }
    // u - v パス上に x が存在するか
    bool is_on_path(int u, int v, int x) { return dist(u, x) + dist(x, v) == dist(u, v); }
    // u - v パス上の頂点のうち, u からの距離が d の頂点
    int jump(int u, int v, int d) {
        int l = lca(u, v);
        int d1 = depth[u] - depth[l];
        // u - l パス上に x が存在する場合
        if (d <= d1) return level_ancestor(u, d);
        int d2 = d1 + depth[v] - depth[l];
        // l - v パス上に x が存在する場合
        if (d <= d2) return level_ancestor(v, d - d1);
        return -1;
    }
};

// ref: https://nyaannyaan.github.io/library/data-structure/hash-map-variable-length.hpp
template <typename Key, typename Val>
struct HashMap {
    using u32 = uint32_t;
    using u64 = uint64_t;

    u32 cap, s;
    vector<Key> keys;
    vector<Val> vals;
    vector<bool> flag;
    u64 r;
    u32 shift;
    Val DefaultValue;

    static u64 rng() {
        u64 m = chrono::duration_cast<chrono::nanoseconds>(chrono::high_resolution_clock::now().time_since_epoch()).count();
        m ^= m >> 16;
        m ^= m << 32;
        return m;
    }

    void reallocate() {
        cap <<= 1;
        vector<Key> k(cap);
        vector<Val> v(cap);
        vector<bool> f(cap);
        u32 sh = shift - 1;
        for (int i = 0; i < (int)flag.size(); i++) {
            if (flag[i]) {
                u32 hash = (u64(keys[i]) * r) >> sh;
                while (f[hash]) hash = (hash + 1) & (cap - 1);
                k[hash] = keys[i];
                v[hash] = vals[i];
                f[hash] = 1;
            }
        }
        keys.swap(k);
        vals.swap(v);
        flag.swap(f);
        --shift;
    }

    explicit HashMap() : cap(8), s(0), keys(cap), vals(cap), flag(cap), r(rng()), shift(64 - __lg(cap)), DefaultValue(Val()) {}

    Val& operator[](const Key& i) {
        u32 hash = (u64(i) * r) >> shift;
        while (true) {
            if (!flag[hash]) {
                if (s + s / 4 >= cap) {
                    reallocate();
                    return (*this)[i];
                }
                keys[hash] = i;
                flag[hash] = 1;
                ++s;
                return vals[hash] = DefaultValue;
            }
            if (keys[hash] == i) return vals[hash];
            hash = (hash + 1) & (cap - 1);
        }
    }

    // exist -> return pointer of Val
    // not exist -> return nullptr
    const Val* find(const Key& i) const {
        u32 hash = (u64(i) * r) >> shift;
        while (true) {
            if (!flag[hash]) return nullptr;
            if (keys[hash] == i) return &(vals[hash]);
            hash = (hash + 1) & (cap - 1);
        }
    }

    // return vector< pair<const Key&, val& > >
    vector<pair<Key, Val>> enumerate() const {
        vector<pair<Key, Val>> ret;
        for (u32 i = 0; i < cap; ++i)
            if (flag[i]) ret.emplace_back(keys[i], vals[i]);
        return ret;
    }

    int size() const { return s; }

    // set default_value
    void set_default(const Val& val) { DefaultValue = val; }
};

/**
 * @brief Hash Map(可変長版)
 * @docs docs/data-structure/hash-map.md
 */

template <class S, class T>
struct DynamicBinaryIndexedTree {
    S N;
    HashMap<S, T> data;
    DynamicBinaryIndexedTree() = default;
    DynamicBinaryIndexedTree(S size) : N(size + 1) {}

    void add(S i, T x) {
        assert(0 <= i && i < N);
        for (++i; i <= N; i += i & -i) data[i] += x;
    }
    void imos(int l, int r, T x) { add(l, x), add(r, -x); }
    T sum(S i) const {
        if (i <= 0) return T{};
        T res{};
        for (; i > 0; i -= i & -i) {
            // std::unordered_map の場合
            // auto p = data.find(i);
            // res += p != data.end() ? p->second : T{};
            auto p = data.find(i);
            res += p ? *p : T{};
        }
        return res;
    }
    T sum(S l, S r) const { return sum(r) - sum(l); }
    T operator[](S i) const { return sum(i + 1) - sum(i); }
    S lower_bound(T w) {
        if (w <= 0) return 0;
        S i = 0;
        for (S k = S(1) << __lg(N); k > 0; k >>= 1) {
            if (i + k <= N && data[i + k] < w) {
                w -= data[i + k];
                i += k;
            }
        }
        return i + 1;
    }
};

constexpr int INF = (1 << 30) - 1;

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);
    int N, M;
    cin >> N >> M;
    int offset = 1000000;
    vector<int> B(N);
    for (int i = 0; i < N; i++) cin >> B[i], B[i] += offset;
    vector<vector<int>> G(N + M);
    for (int i = 0; i < N - 1; i++) {
        int u, v;
        cin >> u >> v, u--, v--;
        G[u].emplace_back(v);
        G[v].emplace_back(u);
    }
    vector<tuple<int, int, int>> query;
    for (int i = 0; i < M; i++) {
        int t;
        cin >> t;
        if (t == 1) {
            int u, w;
            cin >> u >> w, u--, w += offset;
            G[N].emplace_back(u);
            G[u].emplace_back(N);
            B.emplace_back(w);
            N++;
        }
        if (t == 2) {
            int u, v, k;
            cin >> u >> v >> k, u--, v--;
            query.emplace_back(u, v, k);
        }
    }
    M = query.size();

    LowestCommonAncestorDoubling lca(G);

    vector<vector<pair<int, int>>> che(N);
    for (int i = 0; i < M; i++) {
        auto [u, v, k] = query[i];
        che[u].emplace_back(i, 1);  // out
        che[v].emplace_back(i, 1);  // out
        int L = lca.lca(u, v);
        if (L != 0) {
            che[lca.level_ancestor(L, 1)].emplace_back(i, 0);  // in
        }
        che[L].emplace_back(i, 0);  // in
    }

    // 並列二分探索
    // check(i, mid) : クエリ i の u - v パス内の mid 以下の頂点数が k 以上か
    // 最小値が答え
    vector<int> ok(M, 2 * offset + 1), ng(M, -1);
    while (true) {
        bool is_end = true;
        for (int i = 0; i < M; i++) {
            if (ok[i] - ng[i] > 1) is_end = false;
        }
        if (is_end) break;
        vector<int> mid(M);
        for (int i = 0; i < M; i++) mid[i] = midpoint(ok[i], ng[i]);

        // num[i] := クエリ i の u - v パス内の mid[i] 以下の頂点数
        vector<int> num(M);
        // dfs でカウント
        DynamicBinaryIndexedTree<int, int> bit(2 * offset + 1);
        auto dfs = [&](auto&& self, int v, int p) -> void {
            bit.add(B[v], 1);
            for (auto [i, type] : che[v]) {
                if (type == 0) {
                    // in
                    // mid[i] 以下の頂点を引く
                    num[i] -= bit.sum(mid[i] + 1);
                }
                if (type == 1) {
                    // out
                    // mid[i] 以下の頂点を足す
                    num[i] += bit.sum(mid[i] + 1);
                }
            }
            for (auto to : G[v]) {
                if (to == p) continue;
                self(self, to, v);
            }
            bit.add(B[v], -1);
        };
        dfs(dfs, 0, -1);
        for (int i = 0; i < M; i++) {
            auto [u, v, k] = query[i];
            (num[i] >= k ? ok[i] : ng[i]) = mid[i];
        }
    }

    for (int i = 0; i < M; i++) {
        cout << ok[i] - offset << '\n';
    }
}
accuracy: 1
time_ms: 850
memory_kb: 45200
final_rank: 11

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

#ifdef LOCAL
#include <debug.hpp>
#else
#define debug(...)
#endif

struct LowestCommonAncestorDoubling {
    vector<vector<int>> dp;  // dp[k][v] := 頂点 v の 2^k 先の親の頂点
    vector<int> depth;       // depth[v] := 頂点 v の root からの深さ
    int LOG;

    LowestCommonAncestorDoubling(const vector<vector<int>>& G, int root = 0) { build(G, root); }
    void build(const vector<vector<int>>& G, int root) {
        int V = G.size();
        LOG = 1;
        while ((1 << LOG) < V) LOG++;
        dp.assign(LOG, vector<int>(V, -1));
        depth.assign(V, -1);
        dfs(G, root, -1, 0);  // 2^0 先の親は dfs で求める
        for (int k = 0; k < LOG - 1; k++) {
            for (int i = 0; i < V; i++) {
                if (dp[k][i] == -1) {
                    dp[k + 1][i] = dp[k][i];
                } else {
                    dp[k + 1][i] = dp[k][dp[k][i]];
                }
            }
        }
    }
    void dfs(const vector<vector<int>>& G, int pos, int parent, int dist) {
        dp[0][pos] = parent;
        depth[pos] = dist;
        for (auto to : G[pos]) {
            if (to == parent) continue;
            dfs(G, to, pos, dist + 1);
        }
    }
    // 頂点 pos の d 個先の親
    int level_ancestor(int pos, int d) {
        if (depth[pos] < d) return -1;
        for (int i = 0; i < LOG; i++) {
            if ((d >> i) & 1) {
                pos = dp[i][pos];
            }
        }
        return pos;
    }
    // u, v の LCA を求める
    int lca(int u, int v) {
        if (depth[u] < depth[v]) swap(u, v);         // 頂点 u を深い方にする
        u = level_ancestor(u, depth[u] - depth[v]);  // u と v の高さを揃える
        if (u == v) return u;
        // ダブリング上の二分探索
        for (int i = LOG - 1; i >= 0; i--) {
            if (dp[i][u] != dp[i][v]) {
                u = dp[i][u];
                v = dp[i][v];
            }
        }
        return dp[0][u];
    }
    // u - v パスの距離
    int dist(int u, int v) { return depth[u] + depth[v] - 2 * depth[lca(u, v)]; }
    // u - v パス上に x が存在するか
    bool is_on_path(int u, int v, int x) { return dist(u, x) + dist(x, v) == dist(u, v); }
    // u - v パス上の頂点のうち, u からの距離が d の頂点
    int jump(int u, int v, int d) {
        int l = lca(u, v);
        int d1 = depth[u] - depth[l];
        // u - l パス上に x が存在する場合
        if (d <= d1) return level_ancestor(u, d);
        int d2 = d1 + depth[v] - depth[l];
        // l - v パス上に x が存在する場合
        if (d <= d2) return level_ancestor(v, d - d1);
        return -1;
    }
};

// ref: https://nyaannyaan.github.io/library/data-structure/hash-map-variable-length.hpp
template <typename Key, typename Val>
struct HashMap {
    using u32 = uint32_t;
    using u64 = uint64_t;

    u32 cap, s;
    vector<Key> keys;
    vector<Val> vals;
    vector<bool> flag;
    u64 r;
    u32 shift;
    Val DefaultValue;

    static u64 rng() {
        u64 m = chrono::duration_cast<chrono::nanoseconds>(chrono::high_resolution_clock::now().time_since_epoch()).count();
        m ^= m >> 16;
        m ^= m << 32;
        return m;
    }

    void reallocate() {
        cap <<= 1;
        vector<Key> k(cap);
        vector<Val> v(cap);
        vector<bool> f(cap);
        u32 sh = shift - 1;
        for (int i = 0; i < (int)flag.size(); i++) {
            if (flag[i]) {
                u32 hash = (u64(keys[i]) * r) >> sh;
                while (f[hash]) hash = (hash + 1) & (cap - 1);
                k[hash] = keys[i];
                v[hash] = vals[i];
                f[hash] = 1;
            }
        }
        keys.swap(k);
        vals.swap(v);
        flag.swap(f);
        --shift;
    }

    explicit HashMap() : cap(8), s(0), keys(cap), vals(cap), flag(cap), r(rng()), shift(64 - __lg(cap)), DefaultValue(Val()) {}

    Val& operator[](const Key& i) {
        u32 hash = (u64(i) * r) >> shift;
        while (true) {
            if (!flag[hash]) {
                if (s + s / 4 >= cap) {
                    reallocate();
                    return (*this)[i];
                }
                keys[hash] = i;
                flag[hash] = 1;
                ++s;
                return vals[hash] = DefaultValue;
            }
            if (keys[hash] == i) return vals[hash];
            hash = (hash + 1) & (cap - 1);
        }
    }

    // exist -> return pointer of Val
    // not exist -> return nullptr
    const Val* find(const Key& i) const {
        u32 hash = (u64(i) * r) >> shift;
        while (true) {
            if (!flag[hash]) return nullptr;
            if (keys[hash] == i) return &(vals[hash]);
            hash = (hash + 1) & (cap - 1);
        }
    }

    // return vector< pair<const Key&, val& > >
    vector<pair<Key, Val>> enumerate() const {
        vector<pair<Key, Val>> ret;
        for (u32 i = 0; i < cap; ++i)
            if (flag[i]) ret.emplace_back(keys[i], vals[i]);
        return ret;
    }

    int size() const { return s; }

    // set default_value
    void set_default(const Val& val) { DefaultValue = val; }
};

/**
 * @brief Hash Map(可変長版)
 * @docs docs/data-structure/hash-map.md
 */

template <class S, class T>
struct DynamicBinaryIndexedTree {
    S N;
    HashMap<S, T> data;
    DynamicBinaryIndexedTree() = default;
    DynamicBinaryIndexedTree(S size) : N(size + 1) {}

    void add(S i, T x) {
        assert(0 <= i && i < N);
        for (++i; i <= N; i += i & -i) data[i] += x;
    }
    void imos(int l, int r, T x) { add(l, x), add(r, -x); }
    T sum(S i) const {
        if (i <= 0) return T{};
        T res{};
        for (; i > 0; i -= i & -i) {
            // std::unordered_map の場合
            // auto p = data.find(i);
            // res += p != data.end() ? p->second : T{};
            auto p = data.find(i);
            res += p ? *p : T{};
        }
        return res;
    }
    T sum(S l, S r) const { return sum(r) - sum(l); }
    T operator[](S i) const { return sum(i + 1) - sum(i); }
    S lower_bound(T w) {
        if (w <= 0) return 0;
        S i = 0;
        for (S k = S(1) << __lg(N); k > 0; k >>= 1) {
            if (i + k <= N && data[i + k] < w) {
                w -= data[i + k];
                i += k;
            }
        }
        return i + 1;
    }
};

constexpr int INF = (1 << 30) - 1;

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);
    int N, M;
    cin >> N >> M;
    int offset = 1000000;
    vector<int> B(N);
    for (int i = 0; i < N; i++) cin >> B[i], B[i] += offset;
    vector<vector<int>> G(N);
    for (int i = 0; i < N - 1; i++) {
        int u, v;
        cin >> u >> v, u--, v--;
        G[u].emplace_back(v);
        G[v].emplace_back(u);
    }
    vector<tuple<int, int, int>> query;
    for (int i = 0; i < M; i++) {
        int t;
        cin >> t;
        if (t == 1) {
            int u, w;
            cin >> u >> w, u--, w += offset;
            G.resize(++N);
            G[N - 1].emplace_back(u);
            G[u].emplace_back(N - 1);
            B.emplace_back(w);
        }
        if (t == 2) {
            int u, v, k;
            cin >> u >> v >> k, u--, v--;
            query.emplace_back(u, v, k);
        }
    }
    M = query.size();

    LowestCommonAncestorDoubling lca(G);

    vector<vector<pair<int, int>>> che(N);
    for (int i = 0; i < M; i++) {
        auto [u, v, k] = query[i];
        che[u].emplace_back(i, 1);  // out
        che[v].emplace_back(i, 1);  // out
        int L = lca.lca(u, v);
        if (L != 0) {
            che[lca.level_ancestor(L, 1)].emplace_back(i, 0);  // in
        }
        che[L].emplace_back(i, 0);  // in
    }

    // 並列二分探索
    // check(i, mid) : クエリ i の u - v パス内の mid 以下の頂点数が k 以上か
    // 最小値が答え
    vector<int> ok(M, 2 * offset + 1), ng(M, -1);
    while (true) {
        bool is_end = true;
        for (int i = 0; i < M; i++) {
            if (ok[i] - ng[i] > 1) is_end = false;
        }
        if (is_end) break;
        vector<int> mid(M);
        for (int i = 0; i < M; i++) mid[i] = midpoint(ok[i], ng[i]);

        // num[i] := クエリ i の u - v パス内の mid[i] 以下の頂点数
        vector<int> num(M);
        // dfs でカウント
        DynamicBinaryIndexedTree<int, int> bit(2 * offset + 1);
        auto dfs = [&](auto&& self, int v, int p) -> void {
            bit.add(B[v], 1);
            for (auto [i, type] : che[v]) {
                if (type == 0) {
                    // in
                    // mid[i] 以下の頂点を引く
                    num[i] -= bit.sum(mid[i] + 1);
                }
                if (type == 1) {
                    // out
                    // mid[i] 以下の頂点を足す
                    num[i] += bit.sum(mid[i] + 1);
                }
            }
            for (auto to : G[v]) {
                if (to == p) continue;
                self(self, to, v);
            }
            bit.add(B[v], -1);
        };
        dfs(dfs, 0, -1);
        for (int i = 0; i < M; i++) {
            auto [u, v, k] = query[i];
            (num[i] >= k ? ok[i] : ng[i]) = mid[i];
        }
    }

    for (int i = 0; i < M; i++) {
        cout << ok[i] - offset << '\n';
    }
}
accuracy: 1
time_ms: 890
memory_kb: 36024
final_rank: 10

code_snippet: // AOJ #489 Agrarian Development
// 2025.2.19

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

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

int Cin() {  // 整数の入力
	int n = 0, 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 l, r, cnt; };
 
vector<Node> seg;
int segSize = 0;
 
int buildNullNode(){
    Node n; n.l = 0; n.r = 0; n.cnt = 0;
    seg.push_back(n);
    return segSize++;
}
 
int update(int prev, int L, int R, int pos){
    int curr = segSize++;
    seg.push_back(Node());
    seg[curr] = seg[prev];
    seg[curr].cnt = seg[prev].cnt + 1;
    if(L == R) return curr;

    int mid = (L + R) >> 1;
    if(pos <= mid){
        int leftChild = update(seg[prev].l, L, mid, pos);
        seg[curr].l = leftChild;
    } else {
        int rightChild = update(seg[prev].r, mid+1, R, pos);
        seg[curr].r = rightChild;
    }
    return curr;
}
 
int queryKth(int a, int b, int c, int d, int L, int R, int k){
    if(L == R) return L;
    int mid = (L + R) >> 1;
    int cntLeft = seg[ seg[a].l ].cnt + seg[ seg[b].l ].cnt - seg[ seg[c].l ].cnt - seg[ seg[d].l ].cnt;
    if(k <= cntLeft)
        return queryKth(seg[a].l, seg[b].l, seg[c].l, seg[d].l, L, mid, k);
    return queryKth(seg[a].r, seg[b].r, seg[c].r, seg[d].r, mid+1, R, k - cntLeft);
}
 
const int MAXLOG = 20;
 
int main() {
    int N = Cin(), M = Cin();
    int totalNodesInitial = N;
 
    vector<int> initBalance(N+1);
    for (int i = 1; i <= N; i++) initBalance[i] = Cin();
 
    vector<vector<int>> graph(N+1);
    for (int i = 1; i <= N-1; i++){
        int s = Cin(), t = Cin();
        graph[s].push_back(t);
        graph[t].push_back(s);
    }
 
    struct Op {
        int type;
        int u;
        int v;
        int k;
    };
    vector<Op> ops;
    ops.reserve(M);
    vector<int> extraBalances;
    for (int i = 0; i < M; i++){
        int t = Cin();
        if(t == 1){
            int u = Cin(), w = Cin();
            Op op; op.type = 1; op.u = u; op.v = w; op.k = 0;
            ops.push_back(op);
            extraBalances.push_back(w);
        } else {
            int u = Cin(), v = Cin(), k = Cin();
            Op op; op.type = 2; op.u = u; op.v = v; op.k = k;
            ops.push_back(op);
        }
    }
 
    int addCount = extraBalances.size();
    int totalNodes = N + addCount;
 
    vector<int> allBalances;
    allBalances.reserve(totalNodes);
    for (int i = 1; i <= N; i++) allBalances.push_back(initBalance[i]);
    for (int w : extraBalances)  allBalances.push_back(w);
    sort(allBalances.begin(), allBalances.end());
    allBalances.erase(unique(allBalances.begin(), allBalances.end()), allBalances.end());
    int compSize = allBalances.size();
 
    auto getCompressed = [&](int val) -> int {
        int idx = int(lower_bound(allBalances.begin(), allBalances.end(), val) - allBalances.begin()) + 1;
        return idx;
    };
 
    seg.reserve((totalNodes + 5) * 25);
    segSize = 0;
    int nullRoot = buildNullNode();
 
    vector<int> rt(totalNodes+1, 0);
    vector<int> depth(totalNodes+1, 0);
    vector<int> par(totalNodes+1, 0);
 
    vector<vector<int>> up(MAXLOG, vector<int>(totalNodes+1, 0));
 
    depth[1] = 1;
    par[1] = 0;
    rt[1] = update(nullRoot, 1, compSize, getCompressed(initBalance[1]));
    vector<int> stack; stack.push_back(1);
    while(!stack.empty()){
        int u = stack.back();
        stack.pop_back();
        for (int v : graph[u]){
            if(v == par[u]) continue;
            par[v] = u;
            depth[v] = depth[u] + 1;
            rt[v] = update(rt[u], 1, compSize, getCompressed(initBalance[v]));
            stack.push_back(v);
        }
    }
 
    for (int i = 1; i <= N; i++) up[0][i] = par[i];
    for (int j = 1; j < MAXLOG; j++){
        for (int i = 1; i <= N; i++){
            int prev = up[j-1][i];
            if(prev == 0) up[j][i] = 0;
            else up[j][i] = up[j-1][prev];
        }
    }
 
    int curNodes = N;
 
    auto LCA = [&](int a, int b) -> int {
        if(depth[a] < depth[b]) swap(a, b);
        int d = depth[a] - depth[b];
        for (int i = 0; i < MAXLOG; i++){
            if(d & (1 << i)) a = up[i][a];
        }
        if(a == b) return a;
        for (int i = MAXLOG-1; i >= 0; i--){
            if(up[i][a] != up[i][b]){
                a = up[i][a];
                b = up[i][b];
            }
        }
        return par[a];
    };
 
    for(auto &op : ops){
        if(op.type == 1){
            curNodes++;
            int newNode = curNodes;
            int attach = op.u;
            int newBalance = op.v;
            par[newNode] = attach;
            depth[newNode] = depth[attach] + 1;
            rt[newNode] = update(rt[attach], 1, compSize, getCompressed(newBalance));
            if((int)up[0].size() < curNodes+1){
                for (int i = 0; i < MAXLOG; i++) up[i].resize(curNodes+1, 0);
            }
            up[0][newNode] = attach;
            for (int j = 1; j < MAXLOG; j++){
                int prev = up[j-1][newNode];
                if(prev == 0) up[j][newNode] = 0;
                else up[j][newNode] = up[j-1][prev];
            }
        } else {
            int u = op.u, v = op.v, k = op.k;
            int l = LCA(u, v);
            int parL = par[l];
            int idx = queryKth(rt[u], rt[v], rt[l], (parL == 0 ? nullRoot : rt[parL]), 1, compSize, k);
            int ans = allBalances[idx-1];
            Cout(ans);
        }
    }
    return 0;
}
accuracy: 1
time_ms: 70
memory_kb: 42672
final_rank: 2

code_snippet: // AOJ #489 Agrarian Development
// 2025.2.19

#include <bits/stdc++.h>
using namespace std;
 
struct Node { int l, r, cnt; };
 
vector<Node> seg;
int segSize = 0;
 
int buildNullNode(){
    Node n; n.l = 0; n.r = 0; n.cnt = 0;
    seg.push_back(n);
    return segSize++;
}
 
int update(int prev, int L, int R, int pos){
    int curr = segSize++;
    seg.push_back(Node());
    seg[curr] = seg[prev];
    seg[curr].cnt = seg[prev].cnt + 1;
    if(L == R) return curr;

    int mid = (L + R) >> 1;
    if(pos <= mid){
        int leftChild = update(seg[prev].l, L, mid, pos);
        seg[curr].l = leftChild;
    } else {
        int rightChild = update(seg[prev].r, mid+1, R, pos);
        seg[curr].r = rightChild;
    }
    return curr;
}
 
int queryKth(int a, int b, int c, int d, int L, int R, int k){
    if(L == R) return L;
    int mid = (L + R) >> 1;
    int cntLeft = seg[ seg[a].l ].cnt + seg[ seg[b].l ].cnt - seg[ seg[c].l ].cnt - seg[ seg[d].l ].cnt;
    if(k <= cntLeft)
        return queryKth(seg[a].l, seg[b].l, seg[c].l, seg[d].l, L, mid, k);
    return queryKth(seg[a].r, seg[b].r, seg[c].r, seg[d].r, mid+1, R, k - cntLeft);
}
 
const int MAXLOG = 20;
 
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
 
    int N, M;
    cin >> N >> M;
    int totalNodesInitial = N;
 
    vector<int> initBalance(N+1);
    for (int i = 1; i <= N; i++) cin >> initBalance[i];
 
    vector<vector<int>> graph(N+1);
    for (int i = 1; i <= N-1; i++){
        int s, t;
        cin >> s >> t;
        graph[s].push_back(t);
        graph[t].push_back(s);
    }
 
    struct Op {
        int type;
        int u;
        int v;
        int k;
    };
    vector<Op> ops;
    ops.reserve(M);
    vector<int> extraBalances;
    for (int i = 0; i < M; i++){
        int t;
        cin >> t;
        if(t == 1){
            int u, w;
            cin >> u >> w;
            Op op; op.type = 1; op.u = u; op.v = w; op.k = 0;
            ops.push_back(op);
            extraBalances.push_back(w);
        } else {
            int u, v, k;
            cin >> u >> v >> k;
            Op op; op.type = 2; op.u = u; op.v = v; op.k = k;
            ops.push_back(op);
        }
    }
 
    int addCount = extraBalances.size();
    int totalNodes = N + addCount;
 
    vector<int> allBalances;
    allBalances.reserve(totalNodes);
    for (int i = 1; i <= N; i++) allBalances.push_back(initBalance[i]);
    for (int w : extraBalances)  allBalances.push_back(w);
    sort(allBalances.begin(), allBalances.end());
    allBalances.erase(unique(allBalances.begin(), allBalances.end()), allBalances.end());
    int compSize = allBalances.size();
 
    auto getCompressed = [&](int val) -> int {
        int idx = int(lower_bound(allBalances.begin(), allBalances.end(), val) - allBalances.begin()) + 1;
        return idx;
    };
 
    seg.reserve((totalNodes + 5) * 25);
    segSize = 0;
    int nullRoot = buildNullNode();
 
    vector<int> rt(totalNodes+1, 0);
    vector<int> depth(totalNodes+1, 0);
    vector<int> par(totalNodes+1, 0);
 
    vector<vector<int>> up(MAXLOG, vector<int>(totalNodes+1, 0));
 
    depth[1] = 1;
    par[1] = 0;
    rt[1] = update(nullRoot, 1, compSize, getCompressed(initBalance[1]));
    vector<int> stack; stack.push_back(1);
    while(!stack.empty()){
        int u = stack.back();
        stack.pop_back();
        for (int v : graph[u]){
            if(v == par[u]) continue;
            par[v] = u;
            depth[v] = depth[u] + 1;
            rt[v] = update(rt[u], 1, compSize, getCompressed(initBalance[v]));
            stack.push_back(v);
        }
    }
 
    for (int i = 1; i <= N; i++) up[0][i] = par[i];
    for (int j = 1; j < MAXLOG; j++){
        for (int i = 1; i <= N; i++){
            int prev = up[j-1][i];
            if(prev == 0) up[j][i] = 0;
            else up[j][i] = up[j-1][prev];
        }
    }
 
    int curNodes = N;
 
    auto LCA = [&](int a, int b) -> int {
        if(depth[a] < depth[b]) swap(a, b);
        int d = depth[a] - depth[b];
        for (int i = 0; i < MAXLOG; i++){
            if(d & (1 << i)) a = up[i][a];
        }
        if(a == b) return a;
        for (int i = MAXLOG-1; i >= 0; i--){
            if(up[i][a] != up[i][b]){
                a = up[i][a];
                b = up[i][b];
            }
        }
        return par[a];
    };
 
    for(auto &op : ops){
        if(op.type == 1){
            curNodes++;
            int newNode = curNodes;
            int attach = op.u;
            int newBalance = op.v;
            par[newNode] = attach;
            depth[newNode] = depth[attach] + 1;
            rt[newNode] = update(rt[attach], 1, compSize, getCompressed(newBalance));
            if((int)up[0].size() < curNodes+1){
                for (int i = 0; i < MAXLOG; i++) up[i].resize(curNodes+1, 0);
            }
            up[0][newNode] = attach;
            for (int j = 1; j < MAXLOG; j++){
                int prev = up[j-1][newNode];
                if(prev == 0) up[j][newNode] = 0;
                else up[j][newNode] = up[j-1][prev];
            }
        } else {
            int u = op.u, v = op.v, k = op.k;
            int l = LCA(u, v);
            int parL = par[l];
            int idx = queryKth(rt[u], rt[v], rt[l], (parL == 0 ? nullRoot : rt[parL]), 1, compSize, k);
            int ans = allBalances[idx-1];
            cout << ans << endl;
        }
    }
    return 0;
}
accuracy: 1
time_ms: 170
memory_kb: 44500
final_rank: 5

code_snippet: #include <bits/stdc++.h>

using namespace std;

struct LowestCommonAncestor{
    int n;
    const vector<vector<int> > g;
    vector<vector<int> > table;
    vector<int> depth;
    int logk;

    LowestCommonAncestor(const vector<vector<int> > &G) : g(G){
        n = g.size();
        logk = 32 - __builtin_clz(g.size());
        table.assign(logk, vector<int>(n, -1));
        depth.assign(n, -1);
        build();
    }

    void build(int s = 0, bool isforest = false){
        if(isforest){
            for(int i = 0; i < n; ++i){
                if(depth[i] == -1){
                    dfs(i, -1, 0);
                }
            }
        }else{
            dfs(s, -1, 0);
        }
        for(int k = 0; k < logk - 1; ++k){
            for(int i = 1; i <= n - 1; ++i){
                if(table[k][i] == -1){
                    table[k + 1][i] = -1;
                }else{
                    table[k + 1][i] = table[k][table[k][i]];
                }
            }
        }
    }

    void dfs(int now, int pre, int dep){
        table[0][now] = pre;
        depth[now] = dep;
        for(const auto& to: g[now]){
            if(to != pre){
                dfs(to, now, dep + 1);
            }
        }
    }

    int query(int u, int v){
        if(depth[u] < depth[v]) swap(u, v);
        for(int k = logk - 1; k >= 0; --k){
            if(depth[u] - depth[v] & 1 << k){
                u = table[k][u];
            }
        }
        if(u == v) return (u);
        for(int k = logk - 1; k >= 0; --k){
            if(table[k][u] != table[k][v]){
                u = table[k][u];
                v = table[k][v];
            }
        }
        return (table[0][u]);
    }

    int dist(int u, int v){
        return (depth[u] + depth[v] - 2 * depth[query(u, v)]);
    }
};

const int MAX_LOG = 18;

int main() {
    cin.tie(nullptr);
    ios_base::sync_with_stdio(false);

    int n, m;
    cin >> n >> m;

    vector<int> b(n);
    vector<vector<int> > tree(n);

    for (int i = 0; i < n; ++i) {
        cin >> b[i];
    }

    vector<tuple<int, int, int> > queries;

    for (int i = 0; i < n - 1; ++i) {
        int s, t;
        cin >> s >> t;
        --s, --t;
        tree[s].emplace_back(t);
        tree[t].emplace_back(s);
    }

    for (int i = 0; i < m; ++i) {
        int com;
        cin >> com;
        if (com == 1) {
            int u, w;
            cin >> u >> w;
            --u;
            tree[u].emplace_back(tree.size());
            tree.emplace_back(vector{u});
            b.emplace_back(w);
        } else {
            int u, v, k;
            cin >> u >> v >> k;
            --u, --v;
            queries.emplace_back(u, v, k);
        }
    }

    int q = queries.size();
    n = tree.size();

    LowestCommonAncestor LCA(tree);
    vector<int> lca;
    vector<vector<pair<int, int> > > qi(n);

    for (int i = 0; i < q; ++i) {
        auto [u, v, k] = queries[i];
        lca.emplace_back(LCA.query(u, v));
        qi[u].emplace_back(i, 1);
        qi[v].emplace_back(i, 1);
        qi[lca[i]].emplace_back(i, -2);
    }

    auto v = b;
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());

    for (auto &&e : b) e = lower_bound(v.begin(), v.end(), e) - v.begin();

    vector<int> x(q), cnt(q);

    for (int i = MAX_LOG - 1; i >= 0; --i) {
        vector<int> seg(1 << (MAX_LOG - i));
        vector<int> memo(q);
        auto f = [&](auto &&self, int cur, int par) -> void {
            seg[b[cur] >> i]++;
            for (auto [idx, sign] : qi[cur]) {
                memo[idx] += seg[x[idx] >> i] * sign;
            }
            for (auto to : tree[cur]) {
                if (to != par) {
                    self(self, to, cur);
                }
            }
            seg[b[cur] >> i]--;
        };
        f(f, 0, -1);
        for (int j = 0; j < q; ++j) {
            memo[j] += (b[lca[j]] >> i) == (x[j] >> i);
            if (cnt[j] + memo[j] < get<2>(queries[j])) {
                x[j] += 1 << i;
                cnt[j] += memo[j];
            }
        }
    }

    for (int i = 0; i < q; ++i) {
        cout << v[x[i]] << '\n';
    }

    return (0);
}
accuracy: 1
time_ms: 150
memory_kb: 39416
final_rank: 3

code_snippet: #include <bits/stdc++.h>

using namespace std;

template <class Func>
class FixPoint : Func {
public:
    explicit constexpr FixPoint(Func&& f) noexcept : Func(forward<Func>(f)) {}

    template <class... Args>
    constexpr decltype(auto) operator()(Args&&... args) const {
        return Func::operator()(*this, std::forward<Args>(args)...);
    }
};

template <class Func>
static inline constexpr decltype(auto) makeFixPoint(Func&& f) noexcept {
    return FixPoint<Func>{forward<Func>(f)};
}

struct LowestCommonAncestor{
    int n;
    const vector<vector<int> > g;
    vector<vector<int> > table;
    vector<int> depth;
    int logk;

    LowestCommonAncestor(const vector<vector<int> > &G) : g(G){
        n = g.size();
        logk = 32 - __builtin_clz(g.size());
        table.assign(logk, vector<int>(n, -1));
        depth.assign(n, -1);
        build();
    }

    void build(int s = 0, bool isforest = false){
        if(isforest){
            for(int i = 0; i < n; ++i){
                if(depth[i] == -1){
                    dfs(i, -1, 0);
                }
            }
        }else{
            dfs(s, -1, 0);
        }
        for(int k = 0; k < logk - 1; ++k){
            for(int i = 1; i <= n - 1; ++i){
                if(table[k][i] == -1){
                    table[k + 1][i] = -1;
                }else{
                    table[k + 1][i] = table[k][table[k][i]];
                }
            }
        }
    }

    void dfs(int now, int pre, int dep){
        table[0][now] = pre;
        depth[now] = dep;
        for(const auto& to: g[now]){
            if(to != pre){
                dfs(to, now, dep + 1);
            }
        }
    }

    int query(int u, int v){
        if(depth[u] < depth[v]) swap(u, v);
        for(int k = logk - 1; k >= 0; --k){
            if(depth[u] - depth[v] & 1 << k){
                u = table[k][u];
            }
        }
        if(u == v) return (u);
        for(int k = logk - 1; k >= 0; --k){
            if(table[k][u] != table[k][v]){
                u = table[k][u];
                v = table[k][v];
            }
        }
        return (table[0][u]);
    }

    int dist(int u, int v){
        return (depth[u] + depth[v] - 2 * depth[query(u, v)]);
    }
};

const int MAX_LOG = 18;

int main() {
    cin.tie(nullptr);
    ios_base::sync_with_stdio(false);

    int n, m;
    cin >> n >> m;

    vector<int> b(n);
    vector<vector<int> > tree(n);

    for (int i = 0; i < n; ++i) {
        cin >> b[i];
    }

    vector<tuple<int, int, int> > queries;

    for (int i = 0; i < n - 1; ++i) {
        int s, t;
        cin >> s >> t;
        --s, --t;
        tree[s].emplace_back(t);
        tree[t].emplace_back(s);
    }

    for (int i = 0; i < m; ++i) {
        int com;
        cin >> com;
        if (com == 1) {
            int u, w;
            cin >> u >> w;
            --u;
            tree[u].emplace_back(tree.size());
            tree.emplace_back(vector{u});
            b.emplace_back(w);
        } else {
            int u, v, k;
            cin >> u >> v >> k;
            --u, --v;
            queries.emplace_back(u, v, k);
        }
    }

    int q = queries.size();
    n = tree.size();

    LowestCommonAncestor LCA(tree);
    vector<int> lca;
    vector<vector<pair<int, int> > > qi(n);

    for (int i = 0; i < q; ++i) {
        auto [u, v, k] = queries[i];
        lca.emplace_back(LCA.query(u, v));
        qi[u].emplace_back(i, 1);
        qi[v].emplace_back(i, 1);
        qi[lca[i]].emplace_back(i, -2);
    }

    auto v = b;
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());

    for (auto &&e : b) e = lower_bound(v.begin(), v.end(), e) - v.begin();

    vector<int> x(q), cnt(q);

    for (int i = MAX_LOG - 1; i >= 0; --i) {
        vector<int> seg(1 << (MAX_LOG - i));
        vector<int> memo(q);
        auto f = [&](auto &&self, int cur, int par) -> void {
            seg[b[cur] >> i]++;
            for (auto [idx, sign] : qi[cur]) {
                memo[idx] += seg[x[idx] >> i] * sign;
            }
            for (auto to : tree[cur]) {
                if (to != par) {
                    self(self, to, cur);
                }
            }
            seg[b[cur] >> i]--;
        };
        f(f, 0, -1);
        for (int j = 0; j < q; ++j) {
            memo[j] += (b[lca[j]] >> i) == (x[j] >> i);
            if (cnt[j] + memo[j] < get<2>(queries[j])) {
                x[j] += 1 << i;
                cnt[j] += memo[j];
            }
        }
    }

    for (int i = 0; i < q; ++i) {
        cout << v[x[i]] << '\n';
    }

    return (0);
}
accuracy: 1
time_ms: 170
memory_kb: 39844
final_rank: 4

code_snippet: #include <bits/stdc++.h>

using namespace std;

template <class Func>
class FixPoint : Func {
public:
    explicit constexpr FixPoint(Func&& f) noexcept : Func(forward<Func>(f)) {}

    template <class... Args>
    constexpr decltype(auto) operator()(Args&&... args) const {
        return Func::operator()(*this, std::forward<Args>(args)...);
    }
};

template <class Func>
static inline constexpr decltype(auto) makeFixPoint(Func&& f) noexcept {
    return FixPoint<Func>{forward<Func>(f)};
}

struct LowestCommonAncestor{
    int n;
    const vector<vector<int> > g;
    vector<vector<int> > table;
    vector<int> depth;
    int logk;

    LowestCommonAncestor(const vector<vector<int> > &G) : g(G){
        n = g.size();
        logk = 32 - __builtin_clz(g.size());
        table.assign(logk, vector<int>(n, -1));
        depth.assign(n, -1);
        build();
    }

    void build(int s = 0, bool isforest = false){
        if(isforest){
            for(int i = 0; i < n; ++i){
                if(depth[i] == -1){
                    dfs(i, -1, 0);
                }
            }
        }else{
            dfs(s, -1, 0);
        }
        for(int k = 0; k < logk - 1; ++k){
            for(int i = 1; i <= n - 1; ++i){
                if(table[k][i] == -1){
                    table[k + 1][i] = -1;
                }else{
                    table[k + 1][i] = table[k][table[k][i]];
                }
            }
        }
    }

    void dfs(int now, int pre, int dep){
        table[0][now] = pre;
        depth[now] = dep;
        for(const auto& to: g[now]){
            if(to != pre){
                dfs(to, now, dep + 1);
            }
        }
    }

    int query(int u, int v){
        if(depth[u] < depth[v]) swap(u, v);
        for(int k = logk - 1; k >= 0; --k){
            if(depth[u] - depth[v] & 1 << k){
                u = table[k][u];
            }
        }
        if(u == v) return (u);
        for(int k = logk - 1; k >= 0; --k){
            if(table[k][u] != table[k][v]){
                u = table[k][u];
                v = table[k][v];
            }
        }
        return (table[0][u]);
    }

    int dist(int u, int v){
        return (depth[u] + depth[v] - 2 * depth[query(u, v)]);
    }
};

const int MAX_LOG = 18;

int main() {
    cin.tie(nullptr);
    ios_base::sync_with_stdio(false);

    int n, m;
    cin >> n >> m;

    vector<int> b(n);
    vector<vector<int> > tree(n);

    for (int i = 0; i < n; ++i) {
        cin >> b[i];
    }

    vector<tuple<int, int, int> > queries;

    for (int i = 0; i < n - 1; ++i) {
        int s, t;
        cin >> s >> t;
        --s, --t;
        tree[s].emplace_back(t);
        tree[t].emplace_back(s);
    }

    for (int i = 0; i < m; ++i) {
        int com;
        cin >> com;
        if (com == 1) {
            int u, w;
            cin >> u >> w;
            --u;
            tree[u].emplace_back(tree.size());
            tree.emplace_back(vector{u});
            b.emplace_back(w);
        } else {
            int u, v, k;
            cin >> u >> v >> k;
            --u, --v;
            queries.emplace_back(u, v, k);
        }
    }

    int q = queries.size();
    n = tree.size();

    LowestCommonAncestor LCA(tree);
    vector<int> lca;
    vector<vector<pair<int, int> > > qi(n);

    for (int i = 0; i < q; ++i) {
        auto [u, v, k] = queries[i];
        lca.emplace_back(LCA.query(u, v));
        qi[u].emplace_back(i, 1);
        qi[v].emplace_back(i, 1);
        qi[lca[i]].emplace_back(i, -2);
    }

    auto v = b;
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());

    for (auto &&e : b) e = lower_bound(v.begin(), v.end(), e) - v.begin();

    vector<int> x(q), cnt(q);

    for (int i = MAX_LOG - 1; i >= 0; --i) {
        vector<int> seg(1 << (MAX_LOG - i));
        vector<int> memo(q);
        makeFixPoint([&](auto rec, int cur, int par) -> void {
            seg[b[cur] >> i]++;
            for (auto [idx, sign] : qi[cur]) {
                memo[idx] += seg[x[idx] >> i] * sign;
            }
            for (auto to : tree[cur]) {
                if (to != par) {
                    rec(to, cur);
                }
            }
            seg[b[cur] >> i]--;
        }) (0, -1);
        for (int j = 0; j < q; ++j) {
            memo[j] += (b[lca[j]] >> i) == (x[j] >> i);
            if (cnt[j] + memo[j] < get<2>(queries[j])) {
                x[j] += 1 << i;
                cnt[j] += memo[j];
            }
        }
    }

    for (int i = 0; i < q; ++i) {
        cout << v[x[i]] << '\n';
    }

    return (0);
}
accuracy: 1
time_ms: 160
memory_kb: 45772
final_rank: 6

code_snippet: #pragma region Macros
#include <bits/stdc++.h>

#include <atcoder/all>
using namespace std;
using namespace atcoder;
using lint = long long;
using mint = modint998244353;
using ull = unsigned long long;
using ld = long double;
using int128 = __int128_t;
#define all(x) (x).begin(), (x).end()
#define EPS 1e-8
#define uniqv(v) v.erase(unique(all(v)), v.end())
#define OVERLOAD_REP(_1, _2, _3, name, ...) name
#define REP1(i, n) for (auto i = std::decay_t<decltype(n)>{}; (i) != (n); ++(i))
#define REP2(i, l, r) for (auto i = (l); (i) != (r); ++(i))
#define rep(...) OVERLOAD_REP(__VA_ARGS__, REP2, REP1)(__VA_ARGS__)
#define log(x) cout << x << endl
#define logfixed(x) cout << fixed << setprecision(10) << x << endl;
#define logy(bool)             \
    if (bool) {                \
        cout << "Yes" << endl; \
    } else {                   \
        cout << "No" << endl;  \
    }

ostream &operator<<(ostream &dest, __int128_t value) {
    ostream::sentry s(dest);
    if (s) {
        __uint128_t tmp = value < 0 ? -value : value;
        char buffer[128];
        char *d = end(buffer);
        do {
            --d;
            *d = "0123456789"[tmp % 10];
            tmp /= 10;
        } while (tmp != 0);
        if (value < 0) {
            --d;
            *d = '-';
        }
        int len = end(buffer) - d;
        if (dest.rdbuf()->sputn(d, len) != len) {
            dest.setstate(ios_base::badbit);
        }
    }
    return dest;
}

template <typename T>
ostream &operator<<(ostream &os, const vector<T> &v) {
    for (int i = 0; i < (int)v.size(); i++) {
        os << v[i] << (i + 1 != (int)v.size() ? " " : "");
    }
    return os;
}

template <typename T>
ostream &operator<<(ostream &os, const set<T> &set_var) {
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end()) os << " ";
        itr--;
    }
    return os;
}

template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << itr->first << " -> " << itr->second << "\n";
    }
    return os;
}

template <typename T, typename U>
ostream &operator<<(ostream &os, const pair<T, U> &pair_var) {
    os << "(" << pair_var.first << ", " << pair_var.second << ")";
    return os;
}

void out() { cout << '\n'; }
template <class T, class... Ts>
void out(const T &a, const Ts &...b) {
    cout << a;
    (cout << ... << (cout << ' ', b));
    cout << '\n';
}

template <typename T>
istream &operator>>(istream &is, vector<T> &v) {
    for (T &in : v) is >> in;
    return is;
}

inline void in(void) { return; }
template <typename First, typename... Rest>
void in(First &first, Rest &...rest) {
    cin >> first;
    in(rest...);
    return;
}

template <typename T>
bool chmax(T &a, const T &b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template <typename T>
bool chmin(T &a, const T &b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}

vector<lint> dx8 = {1, 1, 0, -1, -1, -1, 0, 1};
vector<lint> dy8 = {0, 1, 1, 1, 0, -1, -1, -1};
vector<lint> dx4 = {1, 0, -1, 0};
vector<lint> dy4 = {0, 1, 0, -1};

#pragma endregion

struct BitVector {
    unsigned sz;
    unsigned blocksize;
    vector<unsigned> bit, sum;

    BitVector() {}

    BitVector(unsigned siz) {
        sz = siz;
        blocksize = (sz + 31) >> 5;
        bit.assign(blocksize, 0U);
        sum.assign(blocksize, 0U);
    }

    inline void set(int k) { bit[k >> 5] |= 1U << (k & 31); }

    inline void build() {
        sum[0] = 0U;
        for (int i = 1; i < blocksize; i++) {
            sum[i] = sum[i - 1] + __builtin_popcount(bit[i - 1]);
        }
    }

    inline bool access(unsigned k) {
        return (bool((bit[k >> 5] >> (k & 31)) & 1));
    }

    inline int rank(int k) {
        return (sum[k >> 5] + __builtin_popcount(bit[k >> 5] & ((1U << (k & 31)) - 1)));
    }
};

template <class T>
class WaveletMatrix {
   private:
   public:
    // コンストラクタ
    WaveletMatrix() {}
    WaveletMatrix(const vector<T> &v) {
        MI = numeric_limits<T>::min();
        MA = numeric_limits<T>::max();
        n = v.size();
        cmp = v;
        sort(cmp.begin(), cmp.end());
        cmp.erase(unique(cmp.begin(), cmp.end()), cmp.end());
        vector<unsigned> compressed(n);
        vector<unsigned> tmpc(n);
        unsigned size_mx = v.size();
        for (unsigned i = 0; i < n; i++) {
            compressed[i] = compress(v[i]);
        }
        bitsize = bit_width(cmp.size());
        b.resize(bitsize);
        zero.assign(bitsize, 0);
        int cur = 0;

        for (unsigned i = 0; i < bitsize; i++) {
            b[i] = BitVector(n + 1);
            cur = 0;
            for (unsigned j = 0; j < n; j++) {
                if (compressed[j] & (1U << (bitsize - i - 1))) {
                    b[i].set(j);
                } else {
                    zero[i]++;
                    tmpc[cur] = compressed[j];
                    cur++;
                }
            }
            b[i].build();

            for (unsigned j = 0; j < n; j++) {
                if (compressed[j] & (1U << (bitsize - i - 1))) {
                    tmpc[cur] = compressed[j];
                    cur++;
                }
            }
            swap(tmpc, compressed);
        }
    }

    // get v[k]
    T access(unsigned k) {
        unsigned res = 0;
        unsigned cur = k;
        for (unsigned i = 0; i < bitsize; i++) {
            if (b[i].access(cur)) {
                res |= (1U << (bitsize - i - 1));
                cur = zero[i] + b[i].rank(cur);
            } else {
                cur -= b[i].rank(cur);
            }
        }
        return cmp[res];
    }

    // v[l,r) の中でk番目(1-origin)に小さい値を返す
    T kth_smallest(unsigned l, unsigned r, unsigned k) {
        unsigned res = 0;
        unsigned rank1_l, rank1_r, num0;
        for (unsigned i = 0; i < bitsize; i++) {
            rank1_l = b[i].rank(l);
            rank1_r = b[i].rank(r);
            num0 = r - l - (rank1_r - rank1_l);
            if (num0 < k) {
                res |= (1U << (bitsize - i - 1));
                l = zero[i] + rank1_l;
                r = zero[i] + rank1_r;
                k -= num0;
            } else {
                l -= rank1_l;
                r -= rank1_r;
            }
        }
        return cmp[res];
    }

    // v[l,r) の中でk番目(1-origin)に大きい値を返す
    T kth_largest(unsigned l, unsigned r, unsigned k) {
        return kth_smallest(l, r, r - l - k + 1);
    }

    // v[l,r) の中で[mink,maxk)に入る値の個数を返す
    unsigned range_freq(int vl, int vr, T mink, T maxk) {
        int D = compress(mink);
        int U = compress(maxk);
        unsigned res = 0;
        auto dfs = [&](auto &rec, int d, int L, int R, int A, int B) -> void {
            if (U <= A or B <= D) return;
            if (D <= A and B <= U) {
                res += (R - L);
                return;
            }
            if (d == bitsize) {
                if (D <= A and A < U) {
                    res += (R - L);
                }
                return;
            }
            int C = (A + B) / 2;
            int rank0_l = L - b[d].rank(L);
            int rank0_r = R - b[d].rank(R);
            int rank1_l = b[d].rank(L) + zero[d];
            int rank1_r = b[d].rank(R) + zero[d];
            rec(rec, d + 1, rank0_l, rank0_r, A, C);
            rec(rec, d + 1, rank1_l, rank1_r, C, B);
        };
        dfs(dfs, 0, vl, vr, 0, 1 << bitsize);
        return res;
    }

    // v[l,r)の中でvalを超えない最大の値を返す
    T prev_value(unsigned l, unsigned r, T val) {
        int num = range_freq(l, r, MI, val);
        if (num == 0) {
            return MA;
        } else {
            return kth_smallest(l, r, num);
        }
    }

    // v[l,r)の中でval以上の最小の値を返す
    T next_value(unsigned l, unsigned r, T val) {
        int num = range_freq(l, r, MI, val);
        if (num == r - l) {
            return MI;
        } else {
            return kth_smallest(l, r, num + 1);
        }
    }
    vector<T> cmp;
    unsigned n;
    unsigned bitsize;
    vector<BitVector> b;
    vector<unsigned> zero;
    T MI, MA;

    inline unsigned compress(const T &x) {
        return lower_bound(cmp.begin(), cmp.end(), x) - begin(cmp);
    }
};

template <typename T>
class hld_wavelet {
   private:
    vector<int> subtree, depth, ind, parent, top;
    vector<bool> seen;
    vector<int> hl;
    int n;
    WaveletMatrix<T> wm;

    int rec_sub(vector<vector<pair<int, int>>> &g, int cur, int d) {
        int sub = 0;
        for (auto nex : g[cur]) {
            if (seen[nex.first]) continue;
            seen[nex.first] = 1;
            parent[nex.first] = cur;
            sub += rec_sub(g, nex.first, d + 1);
        }
        subtree[cur] = sub + 1;
        depth[cur] = d;
        return subtree[cur];
    }

    void rec_hld(vector<vector<pair<int, int>>> &g, int cur) {
        ind[cur] = int(hl.size());
        seen[cur] = 1;
        hl.push_back(cur);
        int sub = 0;
        int idx = -1;
        for (auto nex : g[cur]) {
            if (subtree[nex.first] > sub and !seen[nex.first]) {
                sub = subtree[nex.first];
                idx = nex.first;
            }
        }
        if (idx != -1) {
            top[idx] = top[cur];
            rec_hld(g, idx);
            for (auto nex : g[cur]) {
                if (nex.first != idx and !seen[nex.first]) {
                    top[nex.first] = nex.first;
                    rec_hld(g, nex.first);
                }
            }
        }
    }

    vector<pair<int, int>> decompose(int u, int v) {
        vector<pair<int, int>> res;
        while (top[u] != top[v]) {
            if (depth[top[u]] < depth[top[v]]) swap(u, v);
            res.emplace_back(ind[top[u]], ind[u]);
            u = parent[top[u]];
        }
        if (depth[u] > depth[v]) swap(u, v);
        res.emplace_back(ind[u], ind[v]);
        return res;
    }

   public:
    hld_wavelet(vector<vector<pair<int, int>>> &g, const vector<T> &node_values, int root = 0) {
        n = g.size();
        seen.resize(n, 0);
        subtree.resize(n, 0);
        ind.resize(n, 0);
        depth.resize(n, 0);
        top.resize(n, 0);
        parent.resize(n, -1);
        seen[root] = 1;
        rec_sub(g, root, 0);
        for (int i = 0; i < n; i++) seen[i] = 0;
        seen[root] = 1;
        top[root] = root;
        rec_hld(g, root);
        for (int i = 1; i < n; i++) {
            int prev = hl[i - 1];
            int cur = hl[i];
            if (top[prev] != top[cur]) continue;
        }

        vector<T> reordered_values(n);
        for (int i = 0; i < n; ++i) {
            reordered_values[i] = node_values[hl[i]];
        }
        wm = WaveletMatrix<T>(reordered_values);
    }

    T kth_path(int u, int v, int k) {
        vector<pair<int, int>> segments;
        for (const auto &[s, t] : decompose(u, v)) {
            if (s > t) {
                segments.emplace_back(t, s + 1);
            } else {
                segments.emplace_back(s, t + 1);
            }
        }
        int cnt = 0;
        int p = 0;
        for (int i = 0; i < wm.bitsize; i++) {
            int c = 0;
            for (auto &[l, r] : segments) {
                int l0 = l - wm.b[i].rank(l);
                int r0 = r - wm.b[i].rank(r);
                c += r0 - l0;
            }

            if (cnt + c > k) {
                for (auto &&[l, r] : segments) {
                    int l0 = l - wm.b[i].rank(l);
                    int r0 = r - wm.b[i].rank(r);
                    l = l0;
                    r = r0;
                }
            } else {
                cnt += c;
                p |= (1 << (wm.bitsize - i - 1));

                for (auto &&[l, r] : segments) {
                    int l0 = l - wm.b[i].rank(l);
                    int r0 = r - wm.b[i].rank(r);
                    l += wm.zero[i] - l0;
                    r += wm.zero[i] - r0;
                }
            }
        }
        return wm.cmp[p];
    }
};

int main() {
    cin.tie(0)->sync_with_stdio(0);

    int n, m;
    in(n, m);
    vector<int> nodew(n);
    in(nodew);

    vector<vector<pair<int, int>>> g(n);
    rep(i, n - 1) {
        int s, t;
        in(s, t);
        s--;
        t--;
        g[s].emplace_back(t, 1);
        g[t].emplace_back(s, 1);
    }

    vector<vector<int>> q(m, vector<int>(4));
    rep(i, m) {
        int op, u, v, w, k;
        in(op);
        q[i][0] = op;

        if (op == 1) {
            in(u, w);
            u--;
            q[i][1] = u;
            q[i][2] = w;
            nodew.emplace_back(w);
            g.push_back(vector<pair<int, int>>());
            int newid = g.size() - 1;
            g[newid].emplace_back(u, 1);
            g[u].emplace_back(newid, 1);
        } else if (op == 2) {
            in(u, v, k);
            u--;
            v--;
            k--;
            q[i][1] = u;
            q[i][2] = v;
            q[i][3] = k;
        }
    }

    hld_wavelet<int> h(g, nodew);

    rep(i, m) {
        int op = q[i][0];
        if (op == 2) {
            int u = q[i][1];
            int v = q[i][2];
            int k = q[i][3];
            out(h.kth_path(u, v, k));
        }
    }
}
accuracy: 1
time_ms: 130
memory_kb: 27732
final_rank: 1

code_snippet: #include <bits/stdc++.h>
#include <atcoder/fenwicktree>
using namespace atcoder;
using namespace std;
using ll = long long;

struct Tree : vector<vector<ll>> {
  using vector<vector<ll>>::operator=;

 private:
  void dfs_sz(ll v, ll p) {
    sz[v] = 1;
    ord.emplace_back(v);
    for(ll i = p, x = 0; i != -1;) {
      bl[v][x] = i;
      i = bl[i][x], x++;
    }
    for(auto &nv : (*this)[v]) {
      if(nv == p) {
        if((ll)(*this)[v].size() >= 2 && nv == (*this)[v][0]) { std::swap((*this)[v][0], (*this)[v][1]); }
        else { continue; }
      }
      dp[nv] = dp[v] + 1;
      dfs_sz(nv, v);
      sz[v] += sz[nv];
      if(sz[nv] > sz[(*this)[v][0]]) { std::swap(nv, (*this)[v][0]); }
    }
  }
  void dfs_hld(ll v) {
    down[v] = id++;
    for(auto &nv : (*this)[v]) {
      if(nv == par(v)) { continue; }
      nx[nv] = (nv == (*this)[v][0] ? nx[v] : nv);
      dfs_hld(nv);
    }
    up[v] = id;
  }
  vector<pair<ll, ll>> ascend(ll u, ll v) const {
    vector<pair<ll, ll>> r;
    while(nx[u] != nx[v]) {
      r.emplace_back(down[u], down[nx[u]]);
      u = par(nx[u]);
    }
    if(u != v) { r.emplace_back(down[u], down[v] + 1); }
    return r;
  }
  vector<pair<ll, ll>> descend(ll u, ll v) const {
    if(u == v) { return {}; }
    if(nx[u] == nx[v]) { return {{down[u] + 1, down[v]}}; }
    auto r = descend(u, par(nx[v]));
    r.emplace_back(down[nx[v]], down[v]);
    return r;
  }

 public:
  ll n, root, id = 0;
  vector<array<ll, 24>> bl;
  vector<ll> dp, sz, ord, down, up, nx;
  Tree(ll n_, ll r = 0): n(n_), root(r) { this->resize(n); }
  Tree(const vector<vector<ll>> &g, ll r = 0): n(g.size()), root(r) {
    *this = g;
    build();
  }
  void add_edge(ll u, ll v) {
    (*this)[u].emplace_back(v);
    (*this)[v].emplace_back(u);
  }
  void build() {
    bl.resize(n);
    dp.resize(n);
    sz.resize(n);
    down.assign(n, -1);
    up.assign(n, -1);
    nx.assign(n, root);
    for(auto &v : bl) { ranges::fill(v, -1); }
    dfs_sz(root, -1);
    dfs_hld(root);
  }
  ll depth(ll i) const { return dp[i]; }
  ll par(ll i) const { return i == root ? root : bl[i][0]; }
  ll order(ll i) const { return ord[i]; }
  ll in(ll i) const { return down[i]; }
  ll out(ll i) const { return up[i]; }
  ll size(ll i) const { return sz[i]; }
  ll kth_ancestor(ll i, ll k) const {
    if(dp[i] < k) { return -1; }
    while(k) {
      ll t = __builtin_ctz(k);
      i = bl[i][t], k ^= 1 << t;
    }
    return i;
  }
  ll dis(ll u, ll v) { return dp[u] + dp[v] - dp[lca(u, v)] * 2; }
  bool onpath(ll u, ll v, ll x) { return dis(u, v) == dis(u, x) + dis(x, v); }
  ll nxt(ll u, ll v) const {
    if(dp[u] >= dp[v]) { return par(u); }
    ll x = kth_ancestor(v, dp[v] - dp[u] - 1);
    return bl[x][0] == u ? x : bl[u][0];
  }
  vector<ll> path(ll u, ll v) const {
    vector<ll> pre, suf;
    while(dp[u] > dp[v]) {
      pre.emplace_back(u);
      u = bl[u][0];
    }
    while(dp[u] < dp[v]) {
      suf.emplace_back(v);
      v = bl[v][0];
    }
    while(u != v) {
      pre.emplace_back(u);
      suf.emplace_back(v);
      u = bl[u][0];
      v = bl[v][0];
    }
    pre.emplace_back(u);
    ranges::reverse(suf);
    copy(suf.begin(), suf.end(), back_inserter(pre));
    return pre;
  }
  ll lca(ll u, ll v) {
    while(nx[u] != nx[v]) {
      if(down[u] < down[v]) { std::swap(u, v); }
      u = par(nx[u]);
    }
    return dp[u] < dp[v] ? u : v;
  }
  ll jump(ll u, ll v, ll x) {
    ll lc = lca(u, v), d1 = dp[u] - dp[lc];
    if(x <= d1) { return kth_ancestor(u, x); }
    ll d = d1 + dp[v] - dp[lc];
    if(x <= d) { return kth_ancestor(v, d - x); }
    return -1;
  }
  vector<ll> diameter() {
    ll s = ranges::max_element(dp) - dp.begin();
    vector<ll> d(n, -1);
    d[s] = 0;
    queue<ll> q;
    q.emplace(s);
    while(!q.empty()) {
      ll v = q.front();
      q.pop();
      for(auto &nv : (*this)[v]) {
        if(d[nv] == -1) {
          d[nv] = d[v] + 1;
          q.emplace(nv);
        }
      }
    }
    ll t = ranges::max_element(d) - d.begin();
    return path(t, s);
  }
  template<typename F> void query(ll u, ll v, bool vertex, const F &f) {
    ll l = lca(u, v);
    for(auto &&[a, b] : ascend(u, l)) {
      ll s = a + 1, t = b;
      s > t ? f(t, s) : f(s, t);
    }
    if(vertex) f(down[l], down[l] + 1);
    for(auto &&[a, b] : descend(l, v)) {
      ll s = a, t = b + 1;
      s > t ? f(t, s) : f(s, t);
    }
  }
  template<typename F> void noncommutative_query(ll u, ll v, bool vertex, const F &f) {
    ll l = lca(u, v);
    for(auto &&[a, b] : ascend(u, l)) { f(a + 1, b); }
    if(vertex) { f(down[l], down[l] + 1); }
    for(auto &&[a, b] : descend(l, v)) { f(a, b + 1); }
  }
  template<typename F> void subtree_query(ll u, bool vertex, const F &f) {
    f(down[u] + int(!vertex), up[u]);
  }
};

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  ll N, Q, M = 1000010;
  cin >> N >> Q;

  vector<ll> b(N);
  for(auto &i : b) { cin >> i; }

  vector<pair<ll, ll>> e(N - 1);
  for(auto &[u, v] : e) {
    cin >> u >> v;
    u--, v--;
  }

  vector<tuple<ll, ll, ll>> q;
  while(Q--) {
    ll type;
    cin >> type;
    if(type == 1) {
      ll u, w;
      cin >> u >> w;
      e.emplace_back(N++, --u);
      b.emplace_back(w);
    }
    else {
      ll u, v, k;
      cin >> u >> v >> k;
      q.emplace_back(--u, --v, k);
    }
  }

  Tree t(N);
  for(auto &[u, v] : e) { t.add_edge(u, v); }
  t.build();

  Q = q.size();
  vector<ll> L(Q, -M), R(Q, M);
  vector<vector<ll>> _ask(2 * M), _add(2 * M);
  auto ask{_ask.begin() + M}, add{_add.begin() + M};
  for(ll i = 0; i < N; i++) { add[b[i]].emplace_back(i); }

  while(true) {
    bool fin = true;
    for(ll i = 0; i < Q; i++) {
      if(R[i] - L[i] > 1) {
        ask[(L[i] + R[i]) / 2].emplace_back(i);
        fin = false;
      }
    }
    if(fin) { break; }

    fenwick_tree<ll> f(N);
    for(ll Mid = -M; Mid < M; Mid++) {
      while(!ask[Mid].empty()) {
        ll i = ask[Mid].back();
        auto [u, v, k] = q[i];
        ask[Mid].pop_back();
        ll c = 0;
        t.query(u, v, true, [&](ll l, ll r) { c += f.sum(l, r); });
        c >= k ? R[i] = Mid : L[i] = Mid;
      }
      for(auto &i : add[Mid]) { f.add(t.in(i), 1); }
    }
  }

  for(auto &i : R) { cout << --i << "\n"; }
}
accuracy: 1
time_ms: 1090
memory_kb: 162144
final_rank: 16

code_snippet: #include <bits/stdc++.h>

#include <cassert>
#include <vector>

#include <cassert>
#include <numeric>
#include <type_traits>

namespace atcoder {

namespace internal {

#ifndef _MSC_VER
template<class T> using is_signed_int128 = typename std::conditional<std::is_same<T, __int128_t>::value || std::is_same<T, __int128>::value, std::true_type, std::false_type>::type;
template<class T> using is_unsigned_int128 = typename std::conditional<std::is_same<T, __uint128_t>::value || std::is_same<T, unsigned __int128>::value, std::true_type, std::false_type>::type;
template<class T> using make_unsigned_int128 = typename std::conditional<std::is_same<T, __int128_t>::value, __uint128_t, unsigned __int128>;
template<class T> using is_integral = typename std::conditional<std::is_integral<T>::value || is_signed_int128<T>::value || is_unsigned_int128<T>::value, std::true_type, std::false_type>::type;
template<class T> using is_signed_int = typename std::conditional<(is_integral<T>::value && std::is_signed<T>::value) || is_signed_int128<T>::value, std::true_type, std::false_type>::type;
template<class T> using is_unsigned_int = typename std::conditional<(is_integral<T>::value && std::is_unsigned<T>::value) || is_unsigned_int128<T>::value, std::true_type, std::false_type>::type;
template<class T> using to_unsigned = typename std::conditional<is_signed_int128<T>::value, make_unsigned_int128<T>, typename std::conditional<std::is_signed<T>::value, std::make_unsigned<T>, std::common_type<T>>::type>::type;

#else

template<class T> using is_integral = typename std::is_integral<T>;
template<class T> using is_signed_int = typename std::conditional<is_integral<T>::value && std::is_signed<T>::value, std::true_type, std::false_type>::type;
template<class T> using is_unsigned_int = typename std::conditional<is_integral<T>::value && std::is_unsigned<T>::value, std::true_type, std::false_type>::type;
template<class T> using to_unsigned = typename std::conditional<is_signed_int<T>::value, std::make_unsigned<T>, std::common_type<T>>::type;

#endif

template<class T> using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;
template<class T> using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;
template<class T> using to_unsigned_t = typename to_unsigned<T>::type;

}  // namespace internal

}  // namespace atcoder


namespace atcoder {

template<class T> struct fenwick_tree {
  using U = internal::to_unsigned_t<T>;

 public:
  fenwick_tree(): _n(0) {}
  explicit fenwick_tree(int n): _n(n), data(n) {}
  void add(int p, T x) {
    assert(0 <= p && p < _n);
    p++;
    while(p <= _n) {
      data[p - 1] += U(x);
      p += p & -p;
    }
  }
  T sum(int l, int r) {
    assert(0 <= l && l <= r && r <= _n);
    return sum(r) - sum(l);
  }

 private:
  int _n;
  std::vector<U> data;
  U sum(int r) {
    U s = 0;
    while(r > 0) {
      s += data[r - 1];
      r -= r & -r;
    }
    return s;
  }
};

}  // namespace atcoder

using namespace atcoder;
using namespace std;
using ll = long long;

struct Tree : vector<vector<ll>> {
  using vector<vector<ll>>::operator=;

 private:
  void dfs_sz(ll v, ll p) {
    sz[v] = 1;
    ord.emplace_back(v);
    for(ll i = p, x = 0; i != -1;) {
      bl[v][x] = i;
      i = bl[i][x], x++;
    }
    for(auto &nv : (*this)[v]) {
      if(nv == p) {
        if((ll)(*this)[v].size() >= 2 && nv == (*this)[v][0]) { std::swap((*this)[v][0], (*this)[v][1]); }
        else { continue; }
      }
      dp[nv] = dp[v] + 1;
      dfs_sz(nv, v);
      sz[v] += sz[nv];
      if(sz[nv] > sz[(*this)[v][0]]) { std::swap(nv, (*this)[v][0]); }
    }
  }
  void dfs_hld(ll v) {
    down[v] = id++;
    for(auto &nv : (*this)[v]) {
      if(nv == par(v)) { continue; }
      nx[nv] = (nv == (*this)[v][0] ? nx[v] : nv);
      dfs_hld(nv);
    }
    up[v] = id;
  }
  vector<pair<ll, ll>> ascend(ll u, ll v) const {
    vector<pair<ll, ll>> r;
    while(nx[u] != nx[v]) {
      r.emplace_back(down[u], down[nx[u]]);
      u = par(nx[u]);
    }
    if(u != v) { r.emplace_back(down[u], down[v] + 1); }
    return r;
  }
  vector<pair<ll, ll>> descend(ll u, ll v) const {
    if(u == v) { return {}; }
    if(nx[u] == nx[v]) { return {{down[u] + 1, down[v]}}; }
    auto r = descend(u, par(nx[v]));
    r.emplace_back(down[nx[v]], down[v]);
    return r;
  }

 public:
  ll n, root, id = 0;
  vector<array<ll, 24>> bl;
  vector<ll> dp, sz, ord, down, up, nx;
  Tree(ll n_, ll r = 0): n(n_), root(r) { this->resize(n); }
  Tree(const vector<vector<ll>> &g, ll r = 0): n(g.size()), root(r) {
    *this = g;
    build();
  }
  void add_edge(ll u, ll v) {
    (*this)[u].emplace_back(v);
    (*this)[v].emplace_back(u);
  }
  void build() {
    bl.resize(n);
    dp.resize(n);
    sz.resize(n);
    down.assign(n, -1);
    up.assign(n, -1);
    nx.assign(n, root);
    for(auto &v : bl) { ranges::fill(v, -1); }
    dfs_sz(root, -1);
    dfs_hld(root);
  }
  ll depth(ll i) const { return dp[i]; }
  ll par(ll i) const { return i == root ? root : bl[i][0]; }
  ll order(ll i) const { return ord[i]; }
  ll in(ll i) const { return down[i]; }
  ll out(ll i) const { return up[i]; }
  ll size(ll i) const { return sz[i]; }
  ll kth_ancestor(ll i, ll k) const {
    if(dp[i] < k) { return -1; }
    while(k) {
      ll t = __builtin_ctz(k);
      i = bl[i][t], k ^= 1 << t;
    }
    return i;
  }
  ll dis(ll u, ll v) { return dp[u] + dp[v] - dp[lca(u, v)] * 2; }
  bool onpath(ll u, ll v, ll x) { return dis(u, v) == dis(u, x) + dis(x, v); }
  ll nxt(ll u, ll v) const {
    if(dp[u] >= dp[v]) { return par(u); }
    ll x = kth_ancestor(v, dp[v] - dp[u] - 1);
    return bl[x][0] == u ? x : bl[u][0];
  }
  vector<ll> path(ll u, ll v) const {
    vector<ll> pre, suf;
    while(dp[u] > dp[v]) {
      pre.emplace_back(u);
      u = bl[u][0];
    }
    while(dp[u] < dp[v]) {
      suf.emplace_back(v);
      v = bl[v][0];
    }
    while(u != v) {
      pre.emplace_back(u);
      suf.emplace_back(v);
      u = bl[u][0];
      v = bl[v][0];
    }
    pre.emplace_back(u);
    ranges::reverse(suf);
    copy(suf.begin(), suf.end(), back_inserter(pre));
    return pre;
  }
  ll lca(ll u, ll v) {
    while(nx[u] != nx[v]) {
      if(down[u] < down[v]) { std::swap(u, v); }
      u = par(nx[u]);
    }
    return dp[u] < dp[v] ? u : v;
  }
  ll jump(ll u, ll v, ll x) {
    ll lc = lca(u, v), d1 = dp[u] - dp[lc];
    if(x <= d1) { return kth_ancestor(u, x); }
    ll d = d1 + dp[v] - dp[lc];
    if(x <= d) { return kth_ancestor(v, d - x); }
    return -1;
  }
  vector<ll> diameter() {
    ll s = ranges::max_element(dp) - dp.begin();
    vector<ll> d(n, -1);
    d[s] = 0;
    queue<ll> q;
    q.emplace(s);
    while(!q.empty()) {
      ll v = q.front();
      q.pop();
      for(auto &nv : (*this)[v]) {
        if(d[nv] == -1) {
          d[nv] = d[v] + 1;
          q.emplace(nv);
        }
      }
    }
    ll t = ranges::max_element(d) - d.begin();
    return path(t, s);
  }
  template<typename F> void query(ll u, ll v, bool vertex, const F &f) {
    ll l = lca(u, v);
    for(auto &&[a, b] : ascend(u, l)) {
      ll s = a + 1, t = b;
      s > t ? f(t, s) : f(s, t);
    }
    if(vertex) f(down[l], down[l] + 1);
    for(auto &&[a, b] : descend(l, v)) {
      ll s = a, t = b + 1;
      s > t ? f(t, s) : f(s, t);
    }
  }
  template<typename F> void noncommutative_query(ll u, ll v, bool vertex, const F &f) {
    ll l = lca(u, v);
    for(auto &&[a, b] : ascend(u, l)) { f(a + 1, b); }
    if(vertex) { f(down[l], down[l] + 1); }
    for(auto &&[a, b] : descend(l, v)) { f(a, b + 1); }
  }
  template<typename F> void subtree_query(ll u, bool vertex, const F &f) {
    f(down[u] + int(!vertex), up[u]);
  }
};

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  ll N, Q, M = 1000010;
  cin >> N >> Q;

  vector<ll> b(N);
  for(auto &i : b) { cin >> i; }

  vector<pair<ll, ll>> e(N - 1);
  for(auto &[u, v] : e) {
    cin >> u >> v;
    u--, v--;
  }

  vector<tuple<ll, ll, ll>> q;
  while(Q--) {
    ll type;
    cin >> type;
    if(type == 1) {
      ll u, w;
      cin >> u >> w;
      e.emplace_back(N++, --u);
      b.emplace_back(w);
    }
    else {
      ll u, v, k;
      cin >> u >> v >> k;
      q.emplace_back(--u, --v, k);
    }
  }

  Tree t(N);
  for(auto &[u, v] : e) { t.add_edge(u, v); }
  t.build();

  Q = q.size();
  vector<ll> L(Q, -M), R(Q, M);
  vector<vector<ll>> _ask(2 * M), _add(2 * M);
  auto ask{_ask.begin() + M}, add{_add.begin() + M};
  for(ll i = 0; i < N; i++) { add[b[i]].emplace_back(i); }

  while(true) {
    bool fin = true;
    for(ll i = 0; i < Q; i++) {
      if(R[i] - L[i] > 1) {
        ask[(L[i] + R[i]) / 2].emplace_back(i);
        fin = false;
      }
    }
    if(fin) { break; }

    fenwick_tree<ll> f(N);
    for(ll Mid = -M; Mid < M; Mid++) {
      while(!ask[Mid].empty()) {
        ll i = ask[Mid].back();
        auto [u, v, k] = q[i];
        ask[Mid].pop_back();
        ll c = 0;
        t.query(u, v, true, [&](ll l, ll r) { c += f.sum(l, r); });
        c >= k ? R[i] = Mid : L[i] = Mid;
      }
      for(auto &i : add[Mid]) { f.add(t.in(i), 1); }
    }
  }

  for(auto &i : R) { cout << --i << "\n"; }
}
accuracy: 1
time_ms: 1090
memory_kb: 162144
final_rank: 16

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

struct HLD {
	vector<vector<long long>> g;
	vector<long long> p,s,v,d;
	vector<pair<long long,long long>> a,ran;
	long long n;
	HLD(vector<vector<long long>> g) : n(g.size()), g(g) {
		p.resize(n,-2ll);
		s.resize(n,0ll);
		a.resize(n);
		d.resize(n,-1ll);
		{
			p[0ll] = -1ll;
			d[0ll] = 0ll;
			queue<long long> q;
			q.push(0ll);
			while(!q.empty()){
				long long it = q.front();
				q.pop();
				for(long long i : g[it]){
					if(p[i] != -2ll) continue;
					p[i] = it;
					d[i] = d[it] + 1ll;
					q.push(i);
				}
			}
		}
		siz(0ll);
		ran.push_back({0ll,-1ll});
		dfs(0ll,0ll);
	}
	long long siz(long long x){
		if(s[x] != 0ll) return s[x];
		s[x] = 1ll;
		for(long long i : g[x]){
			if(i == p[x]) continue;
			s[x] += siz(i);
		}
		return s[x];
	}
	void dfs(long long x,long long r){
		a[x] = {(long long)v.size(),r};
		v.push_back(x);
		long long ma = -1ll;
		ran.back().second++;
		for(long long i : g[x]){
			if(i == p[x]) continue;
			if(ma == -1ll || s[ma] < s[i]){
				ma = i;
			}
		}
		if(ma == -1ll) return;
		dfs(ma,r);
		for(long long i : g[x]){
			if(i == p[x] || i == ma) continue;
			ran.push_back({v.size(),v.size() - 1ll});
			dfs(i,i);
		}
	}
	vector<pair<long long,long long>> get(long long u,long long v){
		vector<pair<long long,long long>> res;
		while(a[u].second != a[v].second){
			if(d[a[u].second] <= d[a[v].second]){
				res.push_back({a[a[v].second].first,a[v].first});
				v = p[a[v].second];
			}else{
				res.push_back({a[a[u].second].first,a[u].first});
				u = p[a[u].second];
			}
		}
		res.push_back({min(a[u].first,a[v].first),max(a[u].first,a[v].first)});
		return res;
	}
	long long operator[](long long x){
		return a[x].first;
	}
	long long lca(long long u,long long v){
		return this->v[get(u,v).back().first];
	}
	long long depth(long long x){
		return d[x];
	}
	long long size(){
		return ran.size();
	}
	long long dist(long long u,long long v){
		long long lc = lca(u,v);
		return depth(u) + depth(v) - depth(lc) * 2ll;
	}
};
template <typename T>
struct segtree {
    long long n, size, log;
    vector<T> d;
	function<T(T,T)> op;
	function<T()> e;
    explicit segtree(long long n,function<T(T,T)> op,function<T()> e) : segtree(vector<T>(n, e()),op,e) {}
    explicit segtree(const vector<T>& v,function<T(T,T)> op,function<T()> e) : n((long long)(v.size())),op(op),e(e) {
		size = 1ll;
		while(size < (unsigned long long)(n)) size *= 2ll;
		log = 0ll;
		while(!(size & (1ll << log))) log++;
        d = vector<T>(2ll * size, e());
        for (long long i = 0ll; i < n; i++) d[size + i] = v[i];
        for (long long i = size - 1ll; i >= 1ll; i--) {
			d[i] = op(d[2ll * i],d[2ll * i + 1ll]);
        }
    }

    void set(long long p, T x) {
        p += size;
        d[p] = x;
        for (long long i = 1ll; i <= log; i++){
			d[p >> i] = op(d[(p >> i) * 2ll],d[(p >> i) * 2ll + 1ll]);
		}
    }

    T operator[](long long p) const {
        return d[p + size];
    }

    T operator()(long long l, long long r) const {
        T left = e(), right = e();
		for(l += size,r += size;l < r;l >>= 1ll,r >>= 1ll){
			if(l & 1ll) left = op(left,d[l++]); 
			if(r & 1ll) right = op(d[--r],right);
		}
        return op(left, right);
    }
};
int main(){
	long long n,m;
	cin >> n >> m;
	vector<long long> b(n);
	for(long long i = 0ll;i < n;i++){
		cin >> b[i];
	}
	vector<vector<long long>> g(n,vector<long long>());
	for(long long i = 0ll;i < n - 1ll;i++){
		long long u,v;
		cin >> u >> v;
		u--;v--;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	vector<vector<long long>> w;
	for(long long i = 0ll;i < m;i++){
		long long x;
		cin >> x;
		if(x == 1ll){
			long long u,y;
			cin >> u >> y;
			u--;
			g.push_back({});
			b.push_back(y);
			g[u].push_back(n);
			g[n].push_back(u);
			n++;
		}else{
			long long u,v,k;
			cin >> u >> v >> k;
			u--;v--;
			w.push_back({u,v,k});
		}
	}
	m = w.size();
	HLD hld(g);
	vector<pair<long long,long long>> ans(m,pair<long long,long long>(0ll,n - 1ll));
	vector<long long> p(n);
	iota(p.begin(),p.end(),0ll);
	sort(p.begin(),p.end(),[&](long long x,long long y){return b[x] < b[y];});
	for(long long i = 0ll;i < 20ll;i++){
		vector<pair<long long,long long>> t;
		for(long long j = 0ll;j < m;j++){
			t.push_back({(ans[j].first + ans[j].second) / 2ll,j});
		}
		sort(t.begin(),t.end());
		segtree<long long> seg(n,[](long long a,long long b){return a + b;},[](){return 0ll;});
		long long now = 0ll;
		for(long long j = 0ll;j < n;j++){
			long long pos = hld[p[j]];
			seg.set(pos,1ll);
			while(now < m && t[now].first == j){
				long long cou = 0ll;
				vector<pair<long long,long long>> r = hld.get(w[t[now].second][0ll],w[t[now].second][1ll]);
				for(pair<long long,long long> ran : r){
					cou += seg(ran.first,ran.second + 1ll);
				}
				if(cou < w[t[now].second][2ll]){
					ans[t[now].second].first = t[now].first + 1ll;
				}else{
					ans[t[now].second].second = t[now].first;
				}
				now++;
			}
		}
	}
	for(long long i = 0ll;i < m;i++){
		long long it = b[p[ans[i].first]];
		cout << it << endl;
	}
}
accuracy: 1
time_ms: 1270
memory_kb: 41024
final_rank: 13

code_snippet: #include <bits/stdc++.h>
using namespace std;
struct segtree{
  int n;
  vector<int> node;
  segtree(int N){
    n = 1;
    while(n<N) n*=2;
    node.resize(n*2-1,0);
  }
  void update(int a,int x){
    int i = a+n-1;
    node[i] = x;
    while(i>0){
      i = (i-1)/2;
      node[i] = node[i*2+1]+node[i*2+2];
    }
  }
  int find(int a,int b,int k=0,int l=0,int r=-1){
    if(r<0) r = n;
    if(b<=l||r<=a) return 0;
    if(a<=l&&r<=b) return node[k];
    int vl = find(a,b,2*k+1,l,(l+r)/2);
    int vr = find(a,b,2*k+2,(l+r)/2,r);
    return vl+vr;
  }
};
int main() {
  int N,M;
  cin >> N >> M;
  vector<int> B(N);
  for(int i=0;i<N;i++) cin >> B[i];
  vector<vector<int>> G(N);
  for(int i=0;i<N-1;i++){
    int u,v;
    cin >> u >> v;
    u--;
    v--;
    G[u].push_back(v);
    G[v].push_back(u);
  }
  vector<int> F,T,K;
  for(int i=0;i<M;i++){
    int n;
    cin >> n;
    if(n==1){
      int u,t;
      cin >> u >> t;
      u--;
      G.push_back({});
      G[N].push_back(u);
      G[u].push_back(N);
      B.push_back(t);
      N++;
    }else{
      int u,v,k;
      cin >> u >> v >> k;
      u--;
      v--;
      F.push_back(u);
      T.push_back(v);
      K.push_back(k);
    }
  }
  vector<int> parent(N);
  vector<int> depth(N);
  vector<int> in(N);
  vector<int> out(N);
  depth[0] = 0;
  int num = 0;
  auto dfs = [&](auto dfs,int u,int p,int k)->int{
    in[u] = k++;
    for(auto v:G[u]){
      if(v==p) continue;
      parent[v] = u;
      depth[v] = depth[u]+1;
      k = dfs(dfs,v,u,k);
    }
    out[u] = k++;
    return k;
  };
  dfs(dfs,0,-1,0);
  vector<int> horder(N);
  iota(horder.begin(),horder.end(),0);
  sort(horder.begin(),horder.end(),[&](int a,int b){
    return B[a]<B[b];
  });
  vector<vector<int>> table(20, vector(N, 0));
  table[0] = parent;
  for (int d = 0; d < 19; ++d) {
    for (int i = 0; i < N; ++i) {
      if (table[d][i] == -1) table[d + 1][i] = -1;
      else table[d + 1][i] = table[d][table[d][i]];
    }
  }
  auto calcLCA = [&](int a, int b) {
    if (depth[a] > depth[b]) swap(a, b);
    for (int w = 19; w >= 0; --w) {
      const int o = table[w][b];
      if (o == -1) continue;
      if (depth[o] >= depth[a]) b = o;
    }
    assert(depth[a] == depth[b]);
    if (a == b) return a;
    for (int w = 19; w >= 0; --w) {
      const int na = table[w][a], nb = table[w][b];
      if (na != nb) {
        a = na;
        b = nb;
      }
    }
    return parent[a];
  };
  int Q = F.size();
  vector<int> ok(Q,N-1),ng(Q,-1);
  for(int t=0;t<20;t++){
    segtree seg(2*N);
    vector<int> mid(Q);
    for(int i=0;i<Q;i++){
      if(ok[i]-ng[i]<=1) mid[i] = -1;
      else mid[i] = (ok[i]+ng[i])/2;
    }
    vector<vector<int>> q(N);
    for(int i=0;i<Q;i++){
      if(mid[i]==-1) continue;
      q[mid[i]].push_back(i);
    }
    for(int i=0;i<N;i++){
      int v = horder[i];
      seg.update(in[v],1);
      seg.update(out[v],-1);
      for(auto j:q[i]){
        int val = seg.find(0,in[F[j]]+1)+seg.find(0,in[T[j]]+1)-seg.find(0,in[calcLCA(F[j],T[j])]+1)-seg.find(0,in[calcLCA(F[j],T[j])]);
        if(val>=K[j]){
          ok[j] = mid[j];
        }else{
          ng[j] = mid[j];
        }
      }
    }
  }
  for(int i=0;i<Q;i++){
    cout << B[horder[ok[i]]] << endl;
  }
}
accuracy: 1
time_ms: 1400
memory_kb: 38592
final_rank: 14

code_snippet: #include<iostream>
#include<cstdio>
#include<string>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
#include<set>
#include<cassert>
using namespace std;

template<class T>
void prt(T a) { cout << a << "\n"; }
#define all(a) a.begin(),a.end()
#define rep(i,n) for(int i=0;i<(int)(n);i++)

using llong = long long;
template <typename T>
bool chmin(T& a, T b) {
	if (a <= b)return false;
	a = b;
	return true;
}
template <typename T>
bool chmax(T& a, T b) {
	if (a >= b)return false;
	a = b;
	return true;
}

template<typename T>
class BIT {
public:
	int n;
	vector<T> dat;
	BIT(int n_) :n(n_ + 1), dat(n, 0) {};

	void add(int i, T x) {
		for (int idx = i; idx < n; idx += (idx & -idx))dat[idx] += x;
	}
	//[1,i]
	T sum(int i) {
		T s(0);
		for (int idx = i; idx > 0; idx -= (idx & -idx))s += dat[idx];
		return s;
	}
};

int main() {
	int n, m;
	cin >> n >> m;
	vector<int> b(n);
	rep(i, n)scanf("%d", &b[i]);
	vector<vector<int>> g(n);
	rep(i, n-1) {
		int s, t;
		scanf("%d %d", &s, &t);
		s--; t--;
		g[s].push_back(t);
		g[t].push_back(s);
	}
	using T = tuple<int, int, int>;
	vector<T>que;
	rep(i, m) {
		int f;
		scanf("%d", &f);
		if (f == 1) {
			int u, v;
			scanf("%d %d", &u, &v);
			g.emplace_back();
			u--;
			g.back().emplace_back(u);
			g[u].emplace_back(g.size() - 1);
			b.emplace_back(v);
		}
		else {
			int u, v, k;
			scanf("%d %d %d", &u, &v, &k);
			u--; v--;
			que.emplace_back(u, v, k);
		}
	}
	vector<int> zaa;
	n = b.size();
	m = que.size();
	rep(i, n)zaa.push_back(b[i]);
	sort(all(zaa));
	zaa.erase(unique(all(zaa)), zaa.end());
	rep(i, n)b[i] = lower_bound(all(zaa), b[i]) - zaa.begin();


	vector<vector<int>> doub(24, vector<int>(n, 0));
	vector<int> h(n,0);
	auto f1 = [&](auto f1, int v, int p)->void {
		if (p != -1) {
			doub[0][v] = p;
			h[v] = h[p] + 1;
		}
		else doub[0][v] = v;
		for (auto i : g[v]) {
			if (i == p)continue;
			f1(f1, i, v);
		}
	};
	f1(f1, 0, -1);

	rep(i, 23) {
		rep(j, n) {
			doub[i + 1][j] = doub[i][doub[i][j]];
		}
	}

	auto lca = [&](int a, int b) {
		if (h[a] < h[b])swap(a, b);
		//aの方が深い
		int sa = h[a] - h[b];
		rep(i, 24) {
			if (sa & 1)a = doub[i][a];
			sa >>= 1;
		}
		if (a == b)return a;

		for (int i = 23; i >= 0; i--) {
			if (doub[i][a] != doub[i][b]) {
				a = doub[i][a];
				b = doub[i][b];
			}
		}
		assert(doub[0][a] == doub[0][b]);
		return doub[0][a];
	};

	vector<vector<int>> che(n);
	vector<int> ks(m);
	rep(i, m) {
		auto [u, v, k] = que[i];
		che[u].emplace_back(i);
		che[v].emplace_back(i);
		int lc = lca(u, v);
		if (lc != 0) {
			che[doub[0][lc]].push_back(i + m);
		}
		che[lc].push_back(i + m);
		ks[i] = k;
	}

	//にぶたんの準備
	vector<int> oks(m,zaa.size()), ngs(m,-1);
	//f(x)=x以下の値がk[i]個以上あるか
	while (true) {
		//続けるかの判定
		bool isbre = true;
	/*	rep(i, m) {
			cout << oks[i] << ":" << ngs[i] << " ";
		}
		prt("");*/
		rep(i, m)if (abs(oks[i] - ngs[i]) != 1)isbre = false;
		if (isbre)break;


		vector<int> mids(m);
		rep(i, m)mids[i] = (oks[i] + ngs[i]) / 2;

		/*for (auto i : mids)cout << i << " ";
		prt("");*/
		vector<int> num(m,0);
		//それぞれのクエリでの区間内のmids[i]以下の値の個数

		auto f = [&](auto f, int v, int p, BIT<int>& bi)->void {
			bi.add(b[v]+1, 1);
			for (auto i : che[v]) {
				if (i >= m) {
					num[i - m] -= bi.sum(mids[i-m] + 1);
				//	cout << v << "でnum" << i - m << ":" << -bi.sum(mids[i - m] + 1)<<"\n";
				}
				else {
					num[i] += bi.sum(mids[i] + 1);
					//cout << v << "でnum" << i << ":" << bi.sum(mids[i] + 1)<<"\n";
				}
			}
			for (auto i : g[v]) {
				if (i == p)continue;
				f(f, i, v, bi);
			}
			bi.add(b[v] + 1, -1);
		};
		BIT<int> bi(zaa.size());
		f(f, 0, -1, bi);
	/*	for (auto i : num)cout << i << " ";
		prt("");*/
		rep(i, m) {
			if (num[i] >= ks[i])oks[i] = mids[i];
			else ngs[i] = mids[i];
		}
	}
	rep(i, m) {
		prt(zaa[oks[i]]);
	}
}

/*
小さい方からk番目⇔自分以下の値がk個以上あるような物の中で最小
木の経路上に、「wがx以上のやつが何個あるか」が分かればいい
→自分の先祖にwがx以上のやつが何個あるか　が分かればいい
→座圧して上からBITで集めていけばいい
→並列にぶたん

1回の探索に、セグ木の更新がO(n+m)回、書く場所での計算がO(m)回
log2つつく
*/

/*


5 9
1
3
1
1
4
1 2
2 3
2 4
4 5
1 2 2
1 6 5
1 6 7
1 1 10
2 5 3 1
2 5 3 2
2 5 3 3
2 5 3 4
2 1 2 2
*/
accuracy: 1
time_ms: 270
memory_kb: 38688
final_rank: 7