​ZJOI 2007 捉迷藏 ​​​

一棵树,点权为 0/1,每次修改一个点的点权,询问两个最远的 1 的距离
点分树每个点维护:到当前节点的最远距离,维护最大和次大拼起来的,和一个全局最大的

#include<bits/stdc++.h>
#define N 200050
#define inf 0x3fffffff
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
int n, q, a[N], cnt;
int first[N], nxt[N], to[N], tot;
void add(int x, int y){ nxt[++tot] = first[x], first[x] = tot, to[tot] = y;}
int father[N]; // 点分树
int fa[N][20], dep[N];
int Siz, rt, Maxson[N], siz[N], vis[N];
struct Node{
priority_queue<int> q1, q2;
void Push(int x){ q1.push(x); }
void Delete(int x){ q2.push(x); }
int Top(){
while(q2.size() && q1.top() == q2.top()) q1.pop(), q2.pop();
return q1.top();
}
void Pop(){
while(q2.size() && q1.top() == q2.top()) q1.pop(), q2.pop();
q1.pop();
}
int Top2(){ int val = Top(); Pop(); int ret = Top(); Push(val); return ret;}
int Siz(){ return q1.size() - q2.size();}
}h1[N], h2[N], h3;
void dfs(int u, int f){
for(int i=1; i<=18; i++)
fa[u][i] = fa[fa[u][i-1]][i-1];
for(int i=first[u];i;i=nxt[i]){
int t = to[i]; if(t == f) continue;
dep[t] = dep[u] + 1; fa[t][0] = u; dfs(t, u);
}
}
void getrt(int u, int f){
siz[u] = 1; Maxson[u] = 0;
for(int i=first[u];i;i=nxt[i]){
int t = to[i]; if(t == f || vis[t]) continue;
getrt(t, u); siz[u] += siz[t];
Maxson[u] = max(Maxson[u], siz[t]);
} Maxson[u] = max(Maxson[u], Siz - Maxson[u]);
if(Maxson[u] < Maxson[rt]) rt = u;
}
int lca(int x, int y){
if(dep[x] < dep[y]) swap(x, y);
for(int i=18; i>=0; i--) if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
if(x == y) return x;
for(int i=18; i>=0; i--) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
int Dis(int x, int y){ return dep[x] + dep[y] - 2 * dep[lca(x, y)];}
void dfs2(int u, int fa, int goal){
h1[rt].Push(Dis(u, goal));
for(int i=first[u];i;i=nxt[i]){
int t = to[i]; if(t == fa || vis[t]) continue;
dfs2(t, u, goal);
}
}
void Divide(int u, int fa){
father[u] = fa; vis[u] = 1; h2[u].Push(0);
int sum = Siz;
for(int i=first[u];i;i=nxt[i]){
int t = to[i]; if(vis[t]) continue;
if(siz[t] > siz[u]) Siz = sum - siz[u];
else Siz = siz[t];
rt = 0; getrt(t, 0);
dfs2(rt, 0, u);
h2[u].Push(h1[rt].Top());
Divide(rt, u);
}
if(h2[u].Siz() > 1) h3.Push(h2[u].Top() + h2[u].Top2());
}
void off(int x){
if(h2[x].Siz() > 1) h3.Delete(h2[x].Top() + h2[x].Top2());
h2[x].Delete(0);
if(h2[x].Siz() > 1) h3.Push(h2[x].Top() + h2[x].Top2());
#define fa father[u]
for(int u = x; fa; u = fa){
if(h2[fa].Siz() > 1) h3.Delete(h2[fa].Top() + h2[fa].Top2());
h2[fa].Delete(h1[u].Top());
h1[u].Delete(Dis(x, fa));
if(h1[u].Siz()) h2[fa].Push(h1[u].Top());
if(h2[fa].Siz() > 1) h3.Push(h2[fa].Top() + h2[fa].Top2());
}
}
void on(int x){
if(h2[x].Siz() > 1) h3.Delete(h2[x].Top() + h2[x].Top2());
h2[x].Push(0);
if(h2[x].Siz() > 1) h3.Push(h2[x].Top() + h2[x].Top2());
for(int u = x; fa; u = fa){
if(h2[fa].Siz() > 1) h3.Delete(h2[fa].Top() + h2[fa].Top2());
if(h1[u].Siz()) h2[fa].Delete(h1[u].Top());
h1[u].Push(Dis(x, fa));
h2[fa].Push(h1[u].Top());
if(h2[fa].Siz() > 1) h3.Push(h2[fa].Top() + h2[fa].Top2());
}
#undef fa
}
int main(){
cnt = n = read();
for(int i=1; i<n; i++){
int x = read(), y = read();
add(x, y); add(y, x);
} dep[1] = 1; dfs(1, 0);
Maxson[0] = inf; Siz = n; getrt(1, 0);
Divide(rt, 0);
q = read();
while(q--){
char op[3]; scanf("%s", op);
if(op[0] == 'C'){
int x = read();
if(a[x]) cnt++, on(x);
else cnt--, off(x);
a[x] ^= 1;
}
else{
if(cnt < 2) printf("%d\n", cnt - 1);
else printf("%d\n", h3.Top());
}
} return 0;
}

​BZOJ 2566 xmastree​​​

题意:一棵有边权的树,结点有颜色,每次修改一个点的颜色,求每次修改后所有同色结点的最近距离
类似上一道题,每个颜色用一个 暑假集训 ---- (树上)数据结构_#define 分开维护就可以了
​​​BZOJ3730 震波​​​

建出点分树后可以每个点维护一个树状数组, 表示 u 子树中到 u 距离为 暑假集训 ---- (树上)数据结构_i++_02 的点的权值和
那么 对于u 的子树, 暑假集训 ---- (树上)数据结构_#define_03 的树状数组查一下就可以了。
但这样是算重了的,因为可能到了一个分治中心又走回自己的子树,其实就是正常点分时的差分
于是再维护一个树状数组表示 u 子树中到 fa 距离为暑假集训 ---- (树上)数据结构_ide_04 的点权和
树状数组可以用 vector 动态开点, 这样空间是 暑假集训 ---- (树上)数据结构_i++_05

#include<bits/stdc++.h>
#define N 500050
using namespace std;
int first[N], nxt[N], to[N], tot;
void add(int x, int y){
nxt[++tot] = first[x], first[x] = tot, to[tot] = y;
}
int n, m, val[N];
struct vec{
vector<int> v; int s;
void init(){ v.assign(s+10, 0);}
void update(int x, int val){
x++; for(;x<=s;x+=x&-x) v[x]+=val;
}
int Ask(int x){
x++; x = min(x, s); int res = 0;
for(;x>=1;x-=x&-x) res += v[x]; return res;
}
}f1[N], f2[N];
int st[N][22], sign, id[N], dis[N], lg[N];
void dfs(int u, int f){
st[++sign][0] = dis[u]; id[u] = sign;
for(int i=first[u];i;i=nxt[i]){
int t = to[i]; if(t == f) continue;
dis[t] = dis[u] + 1; dfs(t, u);
st[++sign][0] = dis[u];
}
}
int siz[N], Maxson[N], rt, Siz, vis[N];
void getrt(int u, int f){
siz[u] = 1; Maxson[u] = 0;
for(int i=first[u];i;i=nxt[i]){
int t = to[i]; if(t == f || vis[t]) continue;
getrt(t, u); siz[u] += siz[t];
Maxson[u] = max(Maxson[u], siz[t]);
} Maxson[u] = max(Maxson[u], Siz - siz[u]);
if(Maxson[u] < Maxson[rt]) rt = u;
}
int fa[N], dep[N], d[N], res;
int dist(int u, int v){
int x = id[u], y = id[v];
if(x > y) swap(x, y);
int t = lg[y - x + 1];
return dis[u] + dis[v] - 2 * min(st[x][t], st[y-(1<<t)+1][t]);
}
void getdis(int u, int f){
d[dep[u]] += val[u]; res = max(res, dep[u]);
for(int i=first[u];i;i=nxt[i]){
int t = to[i]; if(t == f || vis[t]) continue;
dep[t] = dep[u] + 1; getdis(t, u);
}
}
void calc(vec &x){
x.s = res + 1; x.init();
for(int i=0; i<=res; i++){
x.update(i, d[i]); d[i] = 0;
} res = 0;
}
void Divide(int u, int f){
fa[u] = f; vis[u] = 1; dep[u] = 0; getdis(u, 0);
calc(f1[u]); int sum = Siz;
for(int i=first[u];i;i=nxt[i]){
int t = to[i]; if(vis[t]) continue;
if(siz[t] > siz[u]) Siz = sum - siz[u];
else Siz = siz[t];
rt = 0; getrt(t, 0); dep[t] = 1; getdis(t, 0);
calc(f2[rt]); dep[t] = 0; Divide(rt, u);
}
}
int ans;
int Qu(int x, int k){
int res = 0;
for(int i=x; i; i = fa[i]){
res += f1[i].Ask(k - dist(i, x));
}
for(int i=x; fa[i]; i = fa[i]){
res -= f2[i].Ask(k - dist(fa[i], x));
} return res;
}
void Modify(int x, int k){
for(int i=x; i; i = fa[i]){
f1[i].update(dist(x, i), k);
}
for(int i=x; fa[i]; i = fa[i]){
f2[i].update(dist(fa[i], x), k);
}
}
int main(){
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++) scanf("%d", &val[i]);
for(int i=1; i<n; i++){
int x, y; scanf("%d%d", &x, &y);
add(x, y); add(y, x);
} dfs(1, 0);
for(int i=2; i<=sign; i++) lg[i] = lg[i>>1] + 1;
for(int i=1; (1<<i) <= sign; i++)
for(int j=1; j+(1<<i)-1 <= sign; j++)
st[j][i] = min(st[j][i-1], st[j+(1<<(i-1))][i-1]);
Siz = n; Maxson[0] = 1e9;
getrt(1, 0);
Divide(rt, 0);
while(m--){
int op, x, y; scanf("%d%d%d", &op, &x, &y); x ^= ans; y ^= ans;
if(op == 0) ans = Qu(x, y), printf("%d\n", ans);
if(op == 1) Modify(x, y - val[x]), val[x] = y;
} return 0;

​[ZJOI2015]幻想乡战略游戏​​​

我们考虑假设当前最优点是x,如果修改了一个点权,就会想某个儿子偏移
我们可以枚举这个儿子(因为度数暑假集训 ---- (树上)数据结构_ide_06), 然后计算在这个儿子的答案,如果比当前小就往儿子走
现在有两个问题:

  1. 如何快速计算在儿子的答案
  2. 往儿子走有可能被卡成暑假集训 ---- (树上)数据结构_#define_07

发现计算一个点的答案,即 暑假集训 ---- (树上)数据结构_#define_08可以用点分树,因为我们只需把 暑假集训 ---- (树上)数据结构_i++_09 统计一遍就可以得到答案
需要维护这些东西:暑假集训 ---- (树上)数据结构_ide_10暑假集训 ---- (树上)数据结构_#define_11
然后当前点的答案就是 暑假集训 ---- (树上)数据结构_#define_12
类似上一道题也会算重,于是同样减一下父亲的贡献
对于第二个问题,我们可以在点分树上走,并不是一个儿子一个儿子的走,而是先确定一个大方向然后跳到下一个分治中心

#include<bits/stdc++.h>
#define N 200050
using namespace std;
typedef long long ll;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
int n, m;
int first[N], nxt[N], to[N], w[N], tot;
void add(int x, int y,int z){
nxt[++tot] = first[x], first[x] = tot;
to[tot] = y; w[tot] = z;
}
ll dis[N], st[N][22];
int lg[N], sign, id[N];
int Siz, siz[N], Maxson[N], rt;
void dfs(int u, int f){
st[++sign][0] = dis[u]; id[u] = sign;
for(int i=first[u];i;i=nxt[i]){
int t = to[i]; if(t == f) continue;
dis[t] = dis[u] + (ll)w[i]; dfs(t, u);
st[++sign][0] = dis[u];
}
}
int vis[N];
ll getdis(int u, int v){
int x = id[u], y = id[v];
if(x > y) swap(x, y);
int t = lg[y-x+1];
return dis[u] + dis[v] - 2 * min(st[x][t], st[y-(1<<t)+1][t]);
}
void getrt(int u, int f){
siz[u] = 1; Maxson[u] = 0;
for(int i=first[u];i;i=nxt[i]){
int t = to[i]; if(t == f || vis[t]) continue;
getrt(t, u); siz[u] += siz[t];
Maxson[u] = max(Maxson[u], siz[t]);
} Maxson[u] = max(Maxson[u], Siz - siz[u]);
if(Maxson[u] < Maxson[rt]) rt = u;
}
int fa[N]; ll sum[N], dis1[N], dis2[N];
vector<pair<int, int> >son[N];
#define mp make_pair
void Divide(int u, int f){
fa[u] = f; vis[u] = 1; int sum = Siz;
for(int i=first[u];i;i=nxt[i]){
int t = to[i]; if(vis[t]) continue;
if(siz[t] > siz[u]) Siz = sum - siz[u];
else Siz = siz[t];
rt = 0; getrt(t, 0); son[u].push_back(mp(t, rt));
Divide(rt, u);
}
}
void update(int u, ll k){
sum[u] += k;
for(int i=u; fa[i]; i = fa[i]){
ll d = getdis(fa[i], u);
dis2[i] += k * d; dis1[fa[i]] += k * d;
sum[fa[i]] += k;
}
}
ll calc(int u){
ll res = dis1[u];
for(int i=u; fa[i]; i = fa[i]){
ll d = getdis(u, fa[i]);
res += dis1[fa[i]] - dis2[i];
res += d * (sum[fa[i]] - sum[i]);
} return res;
}
ll query(int u){
ll ans = calc(u);
for(int i=0; i<son[u].size(); i++){
int t = son[u][i].first, v = son[u][i].second;
ll tmp = calc(t); if(tmp < ans) return query(v);
} return ans;
}
int main(){
n = read(), m = read();
for(int i=1; i<n; i++){
int x = read(), y = read(), z = read();
add(x, y, z); add(y, x, z);
} dfs(1, 0);
for(int i=2; i<=sign; i++) lg[i] = lg[i>>1] + 1;
for(int i=1; (1<<i) <= sign; i++)
for(int j=1; j+(1<<i)-1<=sign; j++)
st[j][i] = min(st[j][i-1], st[j+(1<<(i-1))][i-1]);
Maxson[0] = 1e9; Siz = n; getrt(1, 0); int pre = rt;
Divide(rt, 0); rt = pre;
while(m--){
int u = read(), k = read();
update(u, k);
printf("%lld\n", query(rt));
} return 0;
}

​[Ynoi2011]D1T3​​​

每次找到点分树上最浅的点并且满足它到 x 的路径上的所有点的颜色都在 暑假集训 ---- (树上)数据结构_ide_13 之间
于是问题转换为每次询问一个点,它的子树中有多少个点到它路径上的颜色在 暑假集训 ---- (树上)数据结构_ide_13 之间
我们可以求出路径上颜色的暑假集训 ---- (树上)数据结构_ide_15,就转换为了暑假集训 ---- (树上)数据结构_i++_16
即有多少个颜色存在于一个平面的右上角
对于所有询问 暑假集训 ---- (树上)数据结构_ide_13 按 l 从大到小排序,扫一遍,同种颜色保留最上面的点树状数组查一下即可

#include<bits/stdc++.h>
#define N 100050
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-'0'), ch = getchar();
return cnt * f;
}
int n, m, c[N], exi[N];
int first[N], nxt[N<<1], to[N<<1], tot;
void add(int x, int y){
nxt[++tot] = first[x], first[x] = tot, to[tot] = y;
}
struct node{
int id, l, r, op;
node(int a=0, int b=0, int c=0, int d=0){ id = a; l = b; r = c; op = d;}
bool operator < (const node &a) const{ return r < a.r || (r == a.r && op < a.op); }
} q[N]; int res, ans[N];
int siz[N], mxson[N], rt, Siz, vis[N];
void getrt(int u, int fa){
siz[u] = 1; mxson[u] = 0;
for(int i = first[u]; i; i = nxt[i]){
int t = to[i]; if(t == fa || vis[t]) continue;
getrt(t, u); siz[u] += siz[t];
mxson[u] = max(mxson[u], siz[t]);
} mxson[u] = max(mxson[u], Siz - siz[u]);
if(mxson[u] < mxson[rt]) rt = u;
}
vector<node> v[N];
void dfs(int rot, int u, int fa, int mi, int mx){
mi = min(mi, u); mx = max(mx, u);
v[rot].push_back(node(c[u], mi, mx, 0));
for(int i = first[u]; i; i = nxt[i]){
int t = to[i]; if(t == fa || vis[t]) continue;
dfs(rot, t, u, mi, mx);
}
}
#define pa pair<int,int>
#define mp make_pair
vector<pa > dat[N];
vector<int> fa[N];
void dfs1(int u, int f, int mi, int mx, int rot){
mi = min(mi, u); mx = max(mx, u);
dat[u].push_back(mp(mi, mx));
// cout<<u<<" "<<rot<<" "<<mi<<" "<<mx<<endl;
fa[u].push_back(rot);
for(int i = first[u]; i; i = nxt[i]){
int t = to[i]; if(t == f || vis[t]) continue;
dfs1(t, u, mi, mx, rot);
}
}
void Solve(int u){
vis[u] = 1; dfs(u, u, 0, u, u);
int sz = Siz;
for(int i = first[u]; i; i = nxt[i]){
int t = to[i]; if(vis[t]) continue;
if(siz[t] > siz[u]) Siz = sz - siz[u]; else Siz = siz[t];
rt = 0; getrt(t, u); dfs1(t, 0, u, u, u);
Solve(rt);
}
}
struct BIT{
int c[N];
void Add(int x, int v){ for(;x;x-=x&-x) c[x] += v;}
int Ask(int x){ int ans = 0; for(;x<=n;x+=x&-x) ans += c[x]; return ans;}
}Bit;
int main(){
n = read(), m = read();
for(int i = 1; i <= n; i++) c[i] = read();
for(int i = 1; i < n; i++){
int x = read(), y = read();
add(x, y); add(y, x);
} rt = 0; mxson[0] = n+1; Siz = n; getrt(1, 0);
Solve(rt);
for(int i = 1; i <= m; i++){
int l = read(), r = read(), x = read();
for(int j = 0; j < fa[x].size(); j++){
if(dat[x][j].first >= l && dat[x][j].second <= r){ x = fa[x][j];}
} v[x].push_back(node(i, l, r, 1));
}
for(int x = 1; x <= n; x++){
sort(v[x].begin(), v[x].end());
for(int i = 0; i < v[x].size(); i++){
if(!v[x][i].op){
if(exi[v[x][i].id] < v[x][i].l){
if(exi[v[x][i].id]) Bit.Add(exi[v[x][i].id], -1);
Bit.Add(v[x][i].l, 1);
exi[v[x][i].id] = v[x][i].l;
}
}
else ans[v[x][i].id] = Bit.Ask(v[x][i].l);
}
for(int i = 0; i < v[x].size(); i++){
if(!v[x][i].op){
if(exi[v[x][i].id]) Bit.Add(exi[v[x][i].id], -1), exi[v[x][i].id] = 0;
}
} v[x].clear();
}
for(int i = 1; i <= m; i++) cout << ans[i] << "\n";
return 0;
}

​[Ynoi2011]D2T3​​​ 可以按度数分块,对于 暑假集训 ---- (树上)数据结构_#define_18的不会超过 暑假集训 ---- (树上)数据结构_ide_19 个点,用一个数据结构维护
对于 暑假集训 ---- (树上)数据结构_#define_20 的暴力找
考虑链分治,对于每个点,只维护轻儿子,每次询问先暴力把自己,父亲,重儿子插入,然后查
修改的时候套路一般地改一下重链头父亲的信息就可以了
看一看复杂度,修改 暑假集训 ---- (树上)数据结构_ide_21, 查询 暑假集训 ---- (树上)数据结构_ide_22 的大常数
其实询问的时候只需要查一下 暑假集训 ---- (树上)数据结构_#define_23,然后跟自己父亲重儿子比一下就可以了
只有 4 倍小常数就卡过了

#include<bits/stdc++.h>
#define N 1200050
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-'0'), ch = getchar();
return cnt * f;
}
int n, m;
int first[N], nxt[N<<1], to[N<<1], tot;
void add(int x, int y){ nxt[++tot] = first[x], first[x] = tot, to[tot] = y;}
int a[N];
int c[N];
void Add(int x, int v){ for(;x<=n;x+=x&-x) c[x] += v;}
int Ask(int x){ int ans = 0; for(;x;x-=x&-x) ans += c[x]; return ans;}
int dep[N], fa[N], top[N], siz[N], son[N], id[N], sign;

int rnd(){ return (rand() << 15) | rand();}
struct Treap{
int ch[2], siz, val, rnd, cnt;
}t[N];
#define ls t[x].ch[0]
#define rs t[x].ch[1]
int rt[N], node;
stack<int> bin;
int newnode(int v){
int now = 0;
if(bin.size()) now = bin.top(), bin.pop();
else now = ++node;
t[now].ch[0] = t[now].ch[1] = 0; t[now].siz = t[now].cnt = 1;
t[now].val = v; t[now].rnd = rnd();
return now;
}
void pushup(int x){ t[x].siz = t[ls].siz + t[rs].siz + t[x].cnt;}
void Lrot(int &x){ int y = ls; ls = t[y].ch[1]; t[y].ch[1] = x; t[y].siz = t[x].siz; pushup(x); x = y;}
void Rrot(int &x){ int y = rs; rs = t[y].ch[0]; t[y].ch[0] = x; t[y].siz = t[x].siz; pushup(x); x = y;}
void Insert(int &x, int v){
if(!x){ x = newnode(v); return;} t[x].siz++;
if(v == t[x].val){ t[x].cnt++; return;}
if(v < t[x].val){ Insert(ls, v); if(t[ls].rnd < t[x].rnd) Lrot(x);}
if(v > t[x].val){ Insert(rs, v); if(t[rs].rnd < t[x].rnd) Rrot(x);}
}
void Delete(int &x, int v){
if(!x) return;
if(v == t[x].val){
if(t[x].cnt > 1){ t[x].cnt--; t[x].siz--; return;}
if(!ls || !rs) bin.push(x), x = ls + rs;
else{ if(t[ls].rnd < t[rs].rnd) Lrot(x); else Rrot(x); Delete(x, v);}
return;
} t[x].siz--; if(v < t[x].val) Delete(ls, v);
else Delete(rs, v);
}
int Rank(int x, int k){
if(t[ls].siz >= k) return Rank(ls, k);
if(t[ls].siz + t[x].cnt < k) return Rank(rs, k - t[ls].siz - t[x].cnt);
return t[x].val;
}
void dfs1(int u, int f){
siz[u] = 1;
for(int i = first[u]; i; i = nxt[i]){
int t = to[i]; if(t == f) continue;
fa[t] = u; dep[t] = dep[u] + 1; dfs1(t, u);
siz[u] += siz[t]; if(siz[t] > siz[son[u]]) son[u] = t;
}
}
void dfs2(int u, int Top){
top[u] = Top; id[u] = ++sign; Add(sign, a[u]); Add(sign+1, -a[u]);
if(son[u]) dfs2(son[u], Top);
for(int i = first[u]; i; i = nxt[i]){
int t = to[i]; if(t == fa[u] || t == son[u]) continue;
Insert(rt[u], a[t]); dfs2(t, t);
}
}
void change(int x, int v){
if(!fa[x]) return;
int pre = Ask(id[x]);
Delete(rt[fa[x]], pre);
Insert(rt[fa[x]], pre + v);
}
void modify(int x, int y, int v){
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]]) swap(x, y);
change(top[x], v);
Add(id[top[x]], v); Add(id[x]+1, -v);
x = fa[top[x]];
}
if(id[x] > id[y]) swap(x, y);
if(top[x] == x) change(x, v);
Add(id[x], v); Add(id[y]+1, -v);
}
int tmp[10];
int main(){
srand(time(0)); srand(rand());
n = read(), m = read();
for(int i = 1; i <= n; i++) a[i] = read();
for(int i = 1; i < n; i++){
int x = read(), y = read();
add(x, y); add(y, x);
} dep[1] = 1; dfs1(1, 0); dfs2(1, 1);
while(m--){
int op = read(), x = read(), y = read();
if(op == 1){ int v = read(); modify(x, y, v);}
if(op == 2){
int ret = 0;
if(y <= t[rt[x]].siz)tmp[++ret] = Rank(rt[x], y);
if(y > 1 && y-1 <= t[rt[x]].siz) tmp[++ret] = Rank(rt[x], y-1);
if(y > 2 && y-2 <= t[rt[x]].siz) tmp[++ret] = Rank(rt[x], y-2);
if(y > 3 && y-3 <= t[rt[x]].siz) tmp[++ret] = Rank(rt[x], y-3);
tmp[++ret] = Ask(id[x]);
if(fa[x]) tmp[++ret] = Ask(id[fa[x]]);
if(son[x]) tmp[++ret] = Ask(id[son[x]]);
sort(tmp+1, tmp+ret+1);
if(y <= 3) cout << tmp[y] << "\n";
else cout << tmp[4] << "\n";
}
}
return 0;
}