Problem:
Problem K: Witch Craft Moves
Problem
会津魔法学校は、魔法を使える者が集る学校である。
会津魔法学校には1~
N
までの番号がついた教室と教室同士をつなぐ廊下がある。教室や廊下は何度でも通ることができる。また、会津魔法学校には任意の教室
u
,
v
間を0個以上の教室と廊下を一度ずつ通って移動することができるような経路がただ1つある。
各教室には魔法陣が設置してある。この魔方陣によって、教室に入る度に入った者の魔力が増減させられてしまうのだ。生徒の移動を助けるために以下の二つの質問に応えてほしい。
質問1
0 A B
教室
A
から
B
へ移動したときの魔力の残量を出力してほしい。教室
A
と
B
での増減も含む。また、この質問開始時点での魔力の量は毎回0である。
質問2
1 A C
教室
A
の魔力の増減量が
C
だけ変動させる。
Input
N
cost
1
:
cost
N
a
1
b
1
:
a
N-1
b
N-1
Q
query
1
:
query
Q
入力の最初に教室の数
N
が与えられる。続く
N
行に各教室に入ったときの魔力が増減する量が与えられる。
i
行目の値が
i
番の教室の情報に対応する。続く
N-1
行に廊下の情報が与えられる。
j
行目の廊下は
a
j
番目と
b
j
番目の教室をつなぐことを表す。次に質問の数
Q
が与えられる。続く
Q
行に上記の形式で質問が与えられる。
Output
各質問1について、答えを１行に出力せよ。
Constraints
入力は以下の条件を満たす。
1 ≤
N
≤ 2000
1 ≤
A
i
≤ 2000
1 ≤
B
i
≤ 2000
1 ≤
a
j
,
b
j
≤ N
1 ≤
Q
≤ 1000000
-10000 ≤
C
i
≤ 10000
Sample Input 1
7
1
2
3
4
5
6
7
1 2
1 3
3 4
3 5
5 6
5 7
10
0 1 7
0 2 7
1 1 15
0 1 7
0 2 7
0 1 1
1 1 -15
0 1 7
0 2 7
0 1 1
Sample Output 1
16
18
31
33
16
16
18
1

Submitted solutions:

code_snippet: // AOJ #1536 Witch Craft Moves
// 2025.2.21

#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');
}

const int MAXN = 2005;

vector<int> tree[MAXN];
int cost[MAXN];
int parent[MAXN], depth[MAXN], heavy[MAXN], sizeArr[MAXN];
int head[MAXN], pos[MAXN];
int curPos = 0;

int segtree[4 * MAXN];

int dfs(int u, int p) {
    parent[u] = p;
    depth[u] = (p == -1 ? 0 : depth[p] + 1);
    sizeArr[u] = 1;
    int maxSize = 0;
    heavy[u] = -1;
    for (int v : tree[u]) {
        if (v == p) continue;
        int subSize = dfs(v, u);
        if (subSize > maxSize) {
            maxSize = subSize;
            heavy[u] = v;
        }
        sizeArr[u] += subSize;
    }
    return sizeArr[u];
}

void decompose(int u, int h) {
    head[u] = h;
    pos[u] = curPos++;
    if (heavy[u] != -1) decompose(heavy[u], h);
    for (int v : tree[u]) {
        if (v == parent[u] || v == heavy[u]) continue;
        decompose(v, v);
    }
}

void segtree_build(int idx, int l, int r, const vector<int>& base) {
    if(l == r) {
        segtree[idx] = base[l];
        return;
    }
    int mid = (l + r) / 2;
    segtree_build(idx*2, l, mid, base);
    segtree_build(idx*2+1, mid+1, r, base);
    segtree[idx] = segtree[idx*2] + segtree[idx*2+1];
}

void segtree_update(int idx, int l, int r, int posIdx, int val) {
    if(l == r) {
        segtree[idx] += val;
        return;
    }
    int mid = (l + r) / 2;
    if(posIdx <= mid) segtree_update(idx*2, l, mid, posIdx, val);
    else segtree_update(idx*2+1, mid+1, r, posIdx, val);
    segtree[idx] = segtree[idx*2] + segtree[idx*2+1];
}

int segtree_query(int idx, int l, int r, int ql, int qr) {
    if(ql > r || qr < l) return 0;
    if(ql <= l && r <= qr) return segtree[idx];
    int mid = (l + r) / 2;
    return segtree_query(idx*2, l, mid, ql, qr) + segtree_query(idx*2+1, mid+1, r, ql, qr);
}

int query_path(int u, int v, int n) {
    int res = 0;
    while(head[u] != head[v]) {
        if(depth[head[u]] > depth[head[v]]) swap(u, v);
        res += segtree_query(1, 0, n-1, pos[head[v]], pos[v]);
        v = parent[head[v]];
    }
    if(depth[u] > depth[v]) swap(u, v);
    res += segtree_query(1, 0, n-1, pos[u], pos[v]);
    return res;
}

int main() {
    int N = Cin();
    for (int i = 1; i <= N; i++) cost[i] = Cin();

    for (int i = 1; i <= N-1; i++){
        int u = Cin(), v = Cin();
        tree[u].push_back(v);
        tree[v].push_back(u);
    }
    dfs(1, -1);
    decompose(1, 1);

    vector<int> base(N);
    for (int i = 1; i <= N; i++) base[pos[i]] = cost[i];
    segtree_build(1, 0, N-1, base);

    int Q = Cin();
    while(Q--){
        int t = Cin(), A = Cin(), B = Cin();
        if(t == 0) Cout(query_path(A, B, N));
        else segtree_update(1, 0, N-1, pos[A], B);
    }
    return 0;
}
accuracy: 1
time_ms: 240
memory_kb: 3712
final_rank: 2

code_snippet: // AOJ #1536 Witch Craft Moves
// 2025.2.21

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

const int MAXN = 2005;

vector<int> tree[MAXN];
int cost[MAXN];
int parent[MAXN], depth[MAXN], heavy[MAXN], sizeArr[MAXN];
int head[MAXN], pos[MAXN];
int curPos = 0;

int segtree[4 * MAXN];

int dfs(int u, int p) {
    parent[u] = p;
    depth[u] = (p == -1 ? 0 : depth[p] + 1);
    sizeArr[u] = 1;
    int maxSize = 0;
    heavy[u] = -1;
    for (int v : tree[u]) {
        if (v == p) continue;
        int subSize = dfs(v, u);
        if (subSize > maxSize) {
            maxSize = subSize;
            heavy[u] = v;
        }
        sizeArr[u] += subSize;
    }
    return sizeArr[u];
}

void decompose(int u, int h) {
    head[u] = h;
    pos[u] = curPos++;
    if (heavy[u] != -1) decompose(heavy[u], h);
    for (int v : tree[u]) {
        if (v == parent[u] || v == heavy[u]) continue;
        decompose(v, v);
    }
}

void segtree_build(int idx, int l, int r, const vector<int>& base) {
    if(l == r) {
        segtree[idx] = base[l];
        return;
    }
    int mid = (l + r) / 2;
    segtree_build(idx*2, l, mid, base);
    segtree_build(idx*2+1, mid+1, r, base);
    segtree[idx] = segtree[idx*2] + segtree[idx*2+1];
}

void segtree_update(int idx, int l, int r, int posIdx, int val) {
    if(l == r) {
        segtree[idx] += val;
        return;
    }
    int mid = (l + r) / 2;
    if(posIdx <= mid) segtree_update(idx*2, l, mid, posIdx, val);
    else segtree_update(idx*2+1, mid+1, r, posIdx, val);
    segtree[idx] = segtree[idx*2] + segtree[idx*2+1];
}

int segtree_query(int idx, int l, int r, int ql, int qr) {
    if(ql > r || qr < l) return 0;
    if(ql <= l && r <= qr) return segtree[idx];
    int mid = (l + r) / 2;
    return segtree_query(idx*2, l, mid, ql, qr) + segtree_query(idx*2+1, mid+1, r, ql, qr);
}

int query_path(int u, int v, int n) {
    int res = 0;
    while(head[u] != head[v]) {
        if(depth[head[u]] > depth[head[v]]) swap(u, v);
        res += segtree_query(1, 0, n-1, pos[head[v]], pos[v]);
        v = parent[head[v]];
    }
    if(depth[u] > depth[v]) swap(u, v);
    res += segtree_query(1, 0, n-1, pos[u], pos[v]);
    return res;
}

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

    int N;
    cin >> N;
    for (int i = 1; i <= N; i++) cin >> cost[i];

    for (int i = 1; i <= N-1; i++){
        int u, v;
        cin >> u >> v;
        tree[u].push_back(v);
        tree[v].push_back(u);
    }
    dfs(1, -1);
    decompose(1, 1);

    vector<int> base(N);
    for (int i = 1; i <= N; i++) base[pos[i]] = cost[i];
    segtree_build(1, 0, N-1, base);

    int Q;
    cin >> Q;
    while(Q--){
        int t, A, B;
        cin >> t >> A >> B;
        if(t == 0) cout << query_path(A, B, N) << endl;
        else segtree_update(1, 0, N-1, pos[A], B);
    }
    return 0;
}
accuracy: 1
time_ms: 800
memory_kb: 3732
final_rank: 5

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 1000000000000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;




#define NUM 2005

int N = 1;
int num_node;
int root = 0,euler_index = 0;
int MAX_LOG_V = 13;
int LEFT[NUM],RIGHT[NUM];


int parent[13][NUM],depth[NUM];
vector<int> G[NUM];
ll W[NUM],sum_W[NUM];
ll uniformity[8*NUM],partial[8*NUM];


//★★開区間なので注意★★

void init(ll first_N){
	while(N < first_N)N *= 2;
}

void add(ll left,ll right,ll value,ll node_id,ll node_left,ll node_right){

	if(right <= node_left || left >= node_right)return;
	else if(left <= node_left && right >= node_right){
		uniformity[node_id] += value;
	}else{
		partial[node_id] += (min(right,node_right)-max(left,node_left))*value;
		add(left,right,value,2*node_id+1,node_left,(node_left+node_right)/2);
		add(left,right,value,2*node_id+2,(node_left+node_right)/2,node_right);
	}
}

ll getSum(ll left,ll right,ll node_id,ll node_left,ll node_right){
	if(right <= node_left || left >= node_right)return 0;
	else if(left <= node_left && right >= node_right){
		return (node_right-node_left)*uniformity[node_id]+partial[node_id];

	}else{
		ll sum = (min(right,node_right)-max(left,node_left))*uniformity[node_id];

		sum += getSum(left,right,2*node_id+1,node_left,(node_left+node_right)/2);
		sum += getSum(left,right,2*node_id+2,(node_left+node_right)/2,node_right);
		return sum;
	}
}


void dfs(int node_id,int parent_id,int DEPTH,ll total_W){

	parent[0][node_id] = parent_id;
	depth[node_id] = DEPTH;
	LEFT[node_id] = euler_index++;
	sum_W[node_id] = total_W+W[node_id];

	for(int i = 0; i < G[node_id].size(); i++){
		if(G[node_id][i] == parent_id)continue;
		dfs(G[node_id][i],node_id,DEPTH+1,sum_W[node_id]);
	}
	RIGHT[node_id] = euler_index++;
}

//初期化
void init_lca(){

	dfs(root,-1,0,0);

	for(int k = 0; k + 1 < MAX_LOG_V; k++){
		for(int node_id = 0; node_id < num_node; node_id++){
			if(parent[k][node_id] < 0)parent[k+1][node_id] = -1; //node_idの2^k上のノードがルートノードより上なら、2^(k+1)上も同様とする
			else{
				parent[k+1][node_id] = parent[k][parent[k][node_id]];
			}
		}
	}
}

int lca(int left,int right){

	if(depth[left] > depth[right])swap(left,right);
	for(int k = 0; k < MAX_LOG_V; k++){
		if((depth[right]-depth[left]) >> k & 1){
			right = parent[k][right];
		}
	}
	if(left == right)return left;

	for(int k = MAX_LOG_V-1; k >= 0; k--){
		if(parent[k][left] != parent[k][right]){
			left = parent[k][left];
			right = parent[k][right];
		}
	}
	return parent[0][left];
}

int main(){

	scanf("%d",&num_node);

	for(int i = 0; i < num_node; i++){

		scanf("%lld",&W[i]);
	}


	int from,to;

	for(int i = 0; i < num_node-1; i++){

		scanf("%d %d",&from,&to);
		from--;
		to--;

		G[from].push_back(to);
		G[to].push_back(from);
	}

	init_lca();

	int first_N = RIGHT[root]+1; //セグ木における最下層のノードは、最低RIGHT[0]+1個あれば良い(+1は0オリジンのため)

	init(first_N);

	for(ll i = 0; i <= 2*N-2; i++){
		uniformity[i] = 0;
		partial[i] = 0;
	}

	int num_query;
	scanf("%d",&num_query);

	int command;

	ll A,B,LCA;

	for(int loop = 0; loop < num_query; loop++){

		scanf("%d %lld %lld",&command,&A,&B);

		if(command == 0){

			A--;
			B--;
			LCA = lca(A,B);

			ll cost_A = sum_W[A] + getSum(LEFT[A],LEFT[A]+1,0,0,N);
			ll cost_B = sum_W[B] + getSum(LEFT[B],LEFT[B]+1,0,0,N);
			ll cost_LCA = sum_W[LCA] + getSum(LEFT[LCA],LEFT[LCA]+1,0,0,N);

			printf("%lld\n",cost_A+cost_B-2*cost_LCA+W[LCA]);

		}else{

			A--;
			add(LEFT[A],RIGHT[A]+1,B,0,0,N);
			W[A] += B;
		}
	}

	return 0;
}
accuracy: 1
time_ms: 410
memory_kb: 3704
final_rank: 3

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i))
#define all(x) (x).begin(),(x).end()
#define pb push_back
#define fi first
#define se second
#define dbg(x) cout<<#x" = "<<((x))<<endl
template<class T,class U> ostream& operator<<(ostream& o, const pair<T,U> &p){o<<"("<<p.fi<<","<<p.se<<")";return o;}
template<class T> ostream& operator<<(ostream& o, const vector<T> &v){o<<"[";for(T t:v){o<<t<<",";}o<<"]";return o;}

//ノードの個数
const int MAX_V = 2002;
//ダブリングに必要なサイズ(log(MAX_V))
const int MAX_LOG_V = 11;
//木の隣接リスト表現
vector<int> G[MAX_V];
//根のノード番号
int root = 0;

//親を2^k回辿って到達するノード(根を通り過ぎる場合,-1)
int parent[MAX_LOG_V][MAX_V];
//根からの深さ
int depth[MAX_V];

//現在vに注目、親はp、深さd
void lca_dfs(int v, int p, int d){
    parent[0][v]=p;
    depth[v]=d;
    rep(i,G[v].size()){
        if(G[v][i]!=p){ //親でなければ子
            lca_dfs(G[v][i], v, d+1);
        }
    }
}

//初期化
void lca_init(int V){
    //parent[0]とdepthの初期化
    lca_dfs(root, -1, 0);
    //parentの初期化
    for(int k=0; k+1<MAX_LOG_V; ++k){
        for(int v=0; v<V; ++v){
            if(parent[k][v] < 0) parent[k+1][v]=-1;
            else parent[k+1][v] = parent[k][parent[k][v]];
        }
    }
}

//uとvのLCAを求める
int lca(int u, int v){
    //uとvの深さが同じになるまで親を辿る
    if(depth[u] > depth[v]) swap(u,v);
    for(int k=0; k<MAX_LOG_V; ++k){
        if((depth[v]-depth[u])>>k & 1) v = parent[k][v];
    }
    if(u==v) return u;
    //二分探索でLCAを求める
    for(int k=MAX_LOG_V-1; k>=0; --k){
        if(parent[k][u] != parent[k][v]){
            u=parent[k][u];
            v=parent[k][v];
        }
    }
    return parent[0][u];
}

const int N = 2002;
int n;
int c[N];

// dist from root
int d[N];
void calc_dist(int v, int par){
    for(int e:G[v])if(e!=par){
        d[e] = d[v]+c[e];
        calc_dist(e,v);
    }
}

const int B = 10000;
const int SZ = 100;

int main(){
    scanf(" %d", &n);
    rep(i,n) scanf(" %d", &c[i]);

    rep(i,n-1){
        int a,b;
        scanf(" %d %d", &a, &b);
        --a;
        --b;
        G[a].pb(b);
        G[b].pb(a);
    }

    lca_init(n);

    int q;
    scanf(" %d", &q);

    vector<pair<int,int>> upd;
    rep(i,B){
        for(const auto &p:upd) c[p.fi] += p.se;
        upd.clear();

        d[root] = c[root];
        calc_dist(root, -1);

        rep(j,SZ){
            if(i*SZ + j == q) return 0;

            int type,x,y;
            scanf(" %d %d %d", &type, &x, &y);
            if(type == 0){
                // move x -> y
                --x;
                --y;
                int l = lca(x,y);
                if(l == y) swap(x,y);

                int ans = d[x]+d[y]-c[l];
                int par_l = parent[0][l];
                if(par_l != -1) ans -= 2*d[par_l];

                for(const auto &p:upd){
                    int v = p.fi;
                    int val = p.se;

                    bool on_path = false;
                    int vx = lca(v,x), vy = lca(v,y);
                    if(l == x){
                        if(vy==v && vx==x) on_path = true;
                    }
                    else{
                        if(v==l) on_path = true;
                        else{
                            if(vx != vy){
                                if(vx==l && vy==v) on_path = true;
                                if(vx==v && vy==l) on_path = true;
                            }
                        }
                    }

                    if(on_path) ans += val;
                }

                printf("%d\n", ans);
            }
            else{
                // update c[x] <- y
                --x;
                upd.pb({x,y});
            }
        }
    }


    return 0;
}
accuracy: 1
time_ms: 1970
memory_kb: 3592
final_rank: 7

code_snippet: #include "bits/stdc++.h"

#pragma warning(disable:4996)
using namespace std;
using ld = long double;
typedef long long int Value1;
typedef long long int Value2;
const Value1 Zero1(0);
const Value2 Zero2(0);

struct Node {
	Value1 sum;//更新された値. この値を参照する時は評価が完全に完了しているようにする.
	Value2 lazy;	//遅延されている値を保存している
	Node() :sum(Zero1) {
		lazy = Zero2;
	}
};
struct lazy_segtree {
	int N;
	vector<Node>dat;
	lazy_segtree(int n) :N(1) {
		while (N < n) N *= 2;
		dat.resize(2 * N);
	}

	Value2 lazy_connect(const Value2 l, const Value2 r) {
		return l + r;
	}

	void lazy_func(const int k, const int a, const int b) {

		dat[k].sum += dat[k].lazy*(b - a);

	}

	Value1 connect(const Value1 l, const Value1 r) {
		return l + r;

	}

	// inlineつけないと大変なことになるよ!(遅い)
	inline void lazy_evaluate_node(int k, int a, int b) {
		lazy_func(k, a, b);
		if (k < N) { // 2*k(左の子番号) < 2*N (節点の数) のイメージで. 末端ノードじゃなきゃ伝搬するのと等価.
			dat[2 * k].lazy = lazy_connect(dat[2 * k].lazy, dat[k].lazy);	//次は君が伝搬してね☆って感じ.
			dat[2 * k + 1].lazy = lazy_connect(dat[2 * k + 1].lazy, dat[k].lazy);
		}
		dat[k].lazy = Zero2;
	}

	inline void update_node(int k) { // kの子が既に評価されていることが前提. 末端以外のときしか呼び出さないような位置に書くのでif文要らない.
		dat[k].sum = connect(dat[2 * k].sum, dat[2 * k + 1].sum);

	}

	// update(l,r,v) := [l,r)を更新する. 区間は0-indexed.
	void update(int l, int r, Value2 v, int k = 1, int a = 0, int b = -1) {
		if (b == -1)b = N;
		if (l < 0 || r < 0)assert(false);
		lazy_evaluate_node(k, a, b); 	// とりあえず辿ったノードは都合がいいので伝搬しとけ精神.

		if (b <= l || r <= a) //[a,b)と[l,r)が交差している場合
			return;
		if (l <= a && b <= r) { // [l,r)が[a,b)を完全に含んでいる場合
			dat[k].lazy = lazy_connect(dat[k].lazy, v);
			lazy_evaluate_node(k, a, b); //一回遅延評価しとかないと都合悪いので.
			return;
		}

		int m = (a + b) / 2;
		update(l, r, v, 2 * k, a, m);
		update(l, r, v, 2 * k + 1, m, b);
		update_node(k);
	}
	//get(l,r) := [l,r)に対するクエリの答えを得る. 区間は0-indexed.
	Value1 get(int l, int r, int k = 1, int a = 0, int b = -1) {
		if (b == -1)b = N;

		if (l < 0 || r<0)assert(false);
		lazy_evaluate_node(k, a, b); // とりあえず辿ったノードは都合がいいので伝搬しとけ精神.

		if (b <= l || r <= a) //[a,b)と[l,r)が交差している場合
			return Zero1;

		if (l <= a && b <= r) { // [l,r)が[a,b)を完全に含んでいる場合
			return dat[k].sum;
		}

		int m = (a + b) / 2;
		Value1 vl = get(l, r, 2 * k, a, m);
		Value1 vr = get(l, r, 2 * k + 1, m, b);
		update_node(k);
		return connect(vl, vr);
	}
};

long long int dfs(const long long int num,vector<int>&ls,vector<int>&rs,int &a,vector<int>&sums,const vector<int>&nums,
	const vector<vector<int>>&edges, const int now, const int from) {
	ls[now]=a;
	a++;
	sums[now]=num+nums[now];
	for (auto e : edges[now]) {
		if(e==from)continue;
		else {
			dfs(num+nums[now],ls,rs,a,sums,nums,edges,e,now);
		}
	}
	rs[now]=a;
	return sums[now];
}


class Tree {
public:
	Tree(int V, int root) : V(V), root(root), cnum(V), place(V), id(V) {
		T.resize(V);
		for (int i = 0; i < MAXLOGV; i++) {
			parent[i].resize(V);
		}
		depth.resize(V);
	}
	// uとvをつなぐ
	// lcaを求めることが主目的なので無向グラフとしている
	void unite(int u, int v) {
		T[u].push_back(v);
		T[v].push_back(u);
	}
	void unite(vector<vector<int>>&e) {
		T = e;
	}
	// initする
	// コンストラクタだけじゃなくてこれも呼ばないとlcaが求められないぞ
	void init() {
		dfs(root, 0, 0);
		int id = 0;
		getid(root, 0, id);
	}
	// uとvのlcaを求める
	int lca(int u, int v) const {
		if (depth[u] > depth[v]) swap(u, v);
		for (int k = 0; k < MAXLOGV; k++) {
			if ((depth[v] - depth[u]) >> k & 1) {
				v = parent[k][v];
			}
		}
		if (u == v) return u;
		for (int k = MAXLOGV - 1; k >= 0; k--) {
			if (parent[k][u] != parent[k][v]) {
				u = parent[k][u];
				v = parent[k][v];
			}
		}
		return parent[0][u];
	}
	// uとvの距離を求める
	// edgeを定義しないといけない時はこれじゃダメ
	int dist(int u, int v) const {
		int p = lca(u, v);
		return (depth[u] - depth[p]) + (depth[v] - depth[p]);
	}
	int dfs(int v, int p, int d) {
		parent[0][v] = p;
		depth[v] = d;
		cnum[v] = 0;
		for (int i = 1; i < MAXLOGV; i++) {
			parent[i][v] = parent[i - 1][parent[i - 1][v]];
		}
		for (int next : T[v]) {
			if (next != p) cnum[v] += dfs(next, v, d + 1);
		}
		return cnum[v] + 1;
	}

	void dfs2(int v, int p, vector<vector<int>>&doubles, const vector<int>&nums) {
		for (int next : T[v]) {
			if (next != p) dfs2(next, v, doubles, nums);
		}
		doubles[0][v] = nums[v];
		for (int j = 1; j < MAXLOGV; ++j) {
			doubles[j][v] = min(doubles[j][v], doubles[j - 1][v]);
		}
		for (int j = 0; j < MAXLOGV - 1; ++j) {
			doubles[j + 1][parent[j][v]] = min(doubles[j + 1][parent[j][v]], doubles[j][v]);
		}
	}
	//ここでは親から距離2^iの部分木の最小値を求めている
	vector<vector<int>>get_doubles(const vector<int>&nums) {
		vector<vector<int>>doubles(MAXLOGV, vector<int>(V, 1e9));
		dfs2(root, -1, doubles, nums);
		return doubles;
	}

	void getid(const int v, const int p, int &nplace) {
		place[v] = nplace;
		id[nplace] = v;
		nplace++;
		for (int next : T[v]) {
			if (next != p) getid(next, v, nplace);
		}
	}
	static const int MAXLOGV = 25;
	// グラフの隣接リスト表現
	vector<vector<int> > T;
	// 頂点の数
	int V;
	// 根ノードの番号
	int root;

	// 親ノード
	vector<int> parent[MAXLOGV];
	// 根からの深さ
	vector<int> depth;

	//子の数
	vector<int>cnum;

	//変換
	vector<int>place;
	vector<int>id;

};

int main() {
	int N;cin>>N;
	lazy_segtree seg(N);
	vector<int>ls(N),rs(N),nums(N),sums(N);
	for (int i = 0; i < N; ++i) {
		int a;cin>>a;
		nums[i]=a;
	}
	vector<vector<int>>edges(N);
	for (int i = 0; i < N-1; ++i) {
		int a,b;cin>>a>>b;a--;b--;
		edges[a].push_back(b);
		edges[b].push_back(a);

	}
	int pp=0;
	dfs(0,ls,rs,pp,sums,nums,edges,0,-1);
	for (int i = 0; i < N; ++i) {
		seg.update(ls[i],ls[i]+1,sums[i]);
	}
	Tree tree(N,0);
	tree.unite(edges);
	tree.init();
	int Q;cin>>Q;
	while (Q--) {
		int type;
		cin>>type;
		if (type == 0) {
			int a,b;cin>>a>>b;a--;b--;
			a=ls[a];
			b=ls[b];
			int ab_lca=tree.lca(a,b);
			long long int answer=seg.get(a,a+1)+seg.get(b,b+1)-
				seg.get(ab_lca,ab_lca+1);
			if (ab_lca != 0) {
				answer-=seg.get(tree.parent[0][ab_lca],tree.parent[0][ab_lca]+1);
			}
			cout<<answer<<endl;
		}
		else {
			int a,c;cin>>a>>c;a--;
			seg.update(ls[a],rs[a],c);
		}
	}
	return 0;
}
accuracy: 0.12903225806451613
time_ms: 160
memory_kb: 3428
final_rank: 16

code_snippet: #include "bits/stdc++.h"

#pragma warning(disable:4996)
using namespace std;
using ld = long double;
typedef long long int Value1;
typedef long long int Value2;
const Value1 Zero1(0);
const Value2 Zero2(0);

struct Node {
	Value1 sum;//更新された値. この値を参照する時は評価が完全に完了しているようにする.
	Value2 lazy;	//遅延されている値を保存している
	Node() :sum(Zero1) {
		lazy = Zero2;
	}
};
struct lazy_segtree {
	int N;
	vector<Node>dat;
	lazy_segtree(int n) :N(1) {
		while (N < n) N *= 2;
		dat.resize(2 * N);
	}

	Value2 lazy_connect(const Value2 l, const Value2 r) {
		return l + r;
	}

	void lazy_func(const int k, const int a, const int b) {

		dat[k].sum += dat[k].lazy*(b - a);

	}

	Value1 connect(const Value1 l, const Value1 r) {
		return l + r;

	}

	// inlineつけないと大変なことになるよ!(遅い)
	inline void lazy_evaluate_node(int k, int a, int b) {
		lazy_func(k, a, b);
		if (k < N) { // 2*k(左の子番号) < 2*N (節点の数) のイメージで. 末端ノードじゃなきゃ伝搬するのと等価.
			dat[2 * k].lazy = lazy_connect(dat[2 * k].lazy, dat[k].lazy);	//次は君が伝搬してね☆って感じ.
			dat[2 * k + 1].lazy = lazy_connect(dat[2 * k + 1].lazy, dat[k].lazy);
		}
		dat[k].lazy = Zero2;
	}

	inline void update_node(int k) { // kの子が既に評価されていることが前提. 末端以外のときしか呼び出さないような位置に書くのでif文要らない.
		dat[k].sum = connect(dat[2 * k].sum, dat[2 * k + 1].sum);

	}

	// update(l,r,v) := [l,r)を更新する. 区間は0-indexed.
	void update(int l, int r, Value2 v, int k = 1, int a = 0, int b = -1) {
		if (b == -1)b = N;
		if (l < 0 || r < 0)assert(false);
		lazy_evaluate_node(k, a, b); 	// とりあえず辿ったノードは都合がいいので伝搬しとけ精神.

		if (b <= l || r <= a) //[a,b)と[l,r)が交差している場合
			return;
		if (l <= a && b <= r) { // [l,r)が[a,b)を完全に含んでいる場合
			dat[k].lazy = lazy_connect(dat[k].lazy, v);
			lazy_evaluate_node(k, a, b); //一回遅延評価しとかないと都合悪いので.
			return;
		}

		int m = (a + b) / 2;
		update(l, r, v, 2 * k, a, m);
		update(l, r, v, 2 * k + 1, m, b);
		update_node(k);
	}
	//get(l,r) := [l,r)に対するクエリの答えを得る. 区間は0-indexed.
	Value1 get(int l, int r, int k = 1, int a = 0, int b = -1) {
		if (b == -1)b = N;

		if (l < 0 || r<0)assert(false);
		lazy_evaluate_node(k, a, b); // とりあえず辿ったノードは都合がいいので伝搬しとけ精神.

		if (b <= l || r <= a) //[a,b)と[l,r)が交差している場合
			return Zero1;

		if (l <= a && b <= r) { // [l,r)が[a,b)を完全に含んでいる場合
			return dat[k].sum;
		}

		int m = (a + b) / 2;
		Value1 vl = get(l, r, 2 * k, a, m);
		Value1 vr = get(l, r, 2 * k + 1, m, b);
		update_node(k);
		return connect(vl, vr);
	}
};

long long int dfs(const long long int num,vector<int>&ls,vector<int>&rs,int &a,vector<int>&sums,const vector<int>&nums,
	const vector<vector<int>>&edges, const int now, const int from) {
	ls[now]=a;
	a++;
	sums[now]=num+nums[now];
	for (auto e : edges[now]) {
		if(e==from)continue;
		else {
			dfs(num+nums[now],ls,rs,a,sums,nums,edges,e,now);
		}
	}
	rs[now]=a;
	return sums[now];
}


class Tree {
public:
	Tree(int V, int root) : V(V), root(root), cnum(V), place(V), id(V) {
		T.resize(V);
		for (int i = 0; i < MAXLOGV; i++) {
			parent[i].resize(V);
		}
		depth.resize(V);
	}
	// uとvをつなぐ
	// lcaを求めることが主目的なので無向グラフとしている
	void unite(int u, int v) {
		T[u].push_back(v);
		T[v].push_back(u);
	}
	void unite(vector<vector<int>>&e) {
		T = e;
	}
	// initする
	// コンストラクタだけじゃなくてこれも呼ばないとlcaが求められないぞ
	void init() {
		dfs(root, 0, 0);
		int id = 0;
		getid(root, 0, id);
	}
	// uとvのlcaを求める
	int lca(int u, int v) const {
		if (depth[u] > depth[v]) swap(u, v);
		for (int k = 0; k < MAXLOGV; k++) {
			if ((depth[v] - depth[u]) >> k & 1) {
				v = parent[k][v];
			}
		}
		if (u == v) return u;
		for (int k = MAXLOGV - 1; k >= 0; k--) {
			if (parent[k][u] != parent[k][v]) {
				u = parent[k][u];
				v = parent[k][v];
			}
		}
		return parent[0][u];
	}
	// uとvの距離を求める
	// edgeを定義しないといけない時はこれじゃダメ
	int dist(int u, int v) const {
		int p = lca(u, v);
		return (depth[u] - depth[p]) + (depth[v] - depth[p]);
	}
	int dfs(int v, int p, int d) {
		parent[0][v] = p;
		depth[v] = d;
		cnum[v] = 0;
		for (int i = 1; i < MAXLOGV; i++) {
			parent[i][v] = parent[i - 1][parent[i - 1][v]];
		}
		for (int next : T[v]) {
			if (next != p) cnum[v] += dfs(next, v, d + 1);
		}
		return cnum[v] + 1;
	}

	void dfs2(int v, int p, vector<vector<int>>&doubles, const vector<int>&nums) {
		for (int next : T[v]) {
			if (next != p) dfs2(next, v, doubles, nums);
		}
		doubles[0][v] = nums[v];
		for (int j = 1; j < MAXLOGV; ++j) {
			doubles[j][v] = min(doubles[j][v], doubles[j - 1][v]);
		}
		for (int j = 0; j < MAXLOGV - 1; ++j) {
			doubles[j + 1][parent[j][v]] = min(doubles[j + 1][parent[j][v]], doubles[j][v]);
		}
	}
	//ここでは親から距離2^iの部分木の最小値を求めている
	vector<vector<int>>get_doubles(const vector<int>&nums) {
		vector<vector<int>>doubles(MAXLOGV, vector<int>(V, 1e9));
		dfs2(root, -1, doubles, nums);
		return doubles;
	}

	void getid(const int v, const int p, int &nplace) {
		place[v] = nplace;
		id[nplace] = v;
		nplace++;
		for (int next : T[v]) {
			if (next != p) getid(next, v, nplace);
		}
	}
	static const int MAXLOGV = 25;
	// グラフの隣接リスト表現
	vector<vector<int> > T;
	// 頂点の数
	int V;
	// 根ノードの番号
	int root;

	// 親ノード
	vector<int> parent[MAXLOGV];
	// 根からの深さ
	vector<int> depth;

	//子の数
	vector<int>cnum;

	//変換
	vector<int>place;
	vector<int>id;

};

int main() {
	int N;cin>>N;
	lazy_segtree seg(N);
	vector<int>ls(N),rs(N),nums(N),sums(N);
	for (int i = 0; i < N; ++i) {
		int a;cin>>a;
		nums[i]=a;
	}
	vector<vector<int>>edges(N);
	for (int i = 0; i < N-1; ++i) {
		int a,b;cin>>a>>b;a--;b--;
		edges[a].push_back(b);
		edges[b].push_back(a);

	}
	int pp=0;
	dfs(0,ls,rs,pp,sums,nums,edges,0,-1);
	for (int i = 0; i < N; ++i) {
		seg.update(i,i+1,sums[i]);
	}
	Tree tree(N,0);
	tree.unite(edges);
	tree.init();
	int Q;cin>>Q;
	while (Q--) {
		int type;
		cin>>type;
		if (type == 0) {
			int a,b;cin>>a>>b;a--;b--;
			int ab_lca=tree.lca(a,b);
			long long int answer=seg.get(a,a+1)+seg.get(b,b+1)-
				seg.get(ab_lca,ab_lca+1);
			if (ab_lca != 0) {
				answer-=seg.get(tree.parent[0][ab_lca],tree.parent[0][ab_lca]+1);
			}
			cout<<answer<<endl;
		}
		else {
			int a,c;cin>>a>>c;a--;
			seg.update(ls[a],rs[a],c);
		}
	}
	return 0;
}
accuracy: 0.12903225806451613
time_ms: 160
memory_kb: 3440
final_rank: 18

code_snippet: #include "bits/stdc++.h"

#pragma warning(disable:4996)
using namespace std;
using ld = long double;
typedef long long int Value1;
typedef long long int Value2;
const Value1 Zero1(0);
const Value2 Zero2(0);

struct Node {
	Value1 sum;//更新された値. この値を参照する時は評価が完全に完了しているようにする.
	Value2 lazy;	//遅延されている値を保存している
	Node() :sum(Zero1) {
		lazy = Zero2;
	}
};
struct lazy_segtree {
	int N;
	vector<Node>dat;
	lazy_segtree(int n) :N(1) {
		while (N < n) N *= 2;
		dat.resize(2 * N);
	}

	Value2 lazy_connect(const Value2 l, const Value2 r) {
		return l + r;
	}

	void lazy_func(const int k, const int a, const int b) {

		dat[k].sum += dat[k].lazy*(b - a);

	}

	Value1 connect(const Value1 l, const Value1 r) {
		return l + r;

	}

	// inlineつけないと大変なことになるよ!(遅い)
	inline void lazy_evaluate_node(int k, int a, int b) {
		lazy_func(k, a, b);
		if (k < N) { // 2*k(左の子番号) < 2*N (節点の数) のイメージで. 末端ノードじゃなきゃ伝搬するのと等価.
			dat[2 * k].lazy = lazy_connect(dat[2 * k].lazy, dat[k].lazy);	//次は君が伝搬してね☆って感じ.
			dat[2 * k + 1].lazy = lazy_connect(dat[2 * k + 1].lazy, dat[k].lazy);
		}
		dat[k].lazy = Zero2;
	}

	inline void update_node(int k) { // kの子が既に評価されていることが前提. 末端以外のときしか呼び出さないような位置に書くのでif文要らない.
		dat[k].sum = connect(dat[2 * k].sum, dat[2 * k + 1].sum);

	}

	// update(l,r,v) := [l,r)を更新する. 区間は0-indexed.
	void update(int l, int r, Value2 v, int k = 1, int a = 0, int b = -1) {
		if (b == -1)b = N;
		if (l < 0 || r < 0)assert(false);
		lazy_evaluate_node(k, a, b); 	// とりあえず辿ったノードは都合がいいので伝搬しとけ精神.

		if (b <= l || r <= a) //[a,b)と[l,r)が交差している場合
			return;
		if (l <= a && b <= r) { // [l,r)が[a,b)を完全に含んでいる場合
			dat[k].lazy = lazy_connect(dat[k].lazy, v);
			lazy_evaluate_node(k, a, b); //一回遅延評価しとかないと都合悪いので.
			return;
		}

		int m = (a + b) / 2;
		update(l, r, v, 2 * k, a, m);
		update(l, r, v, 2 * k + 1, m, b);
		update_node(k);
	}
	//get(l,r) := [l,r)に対するクエリの答えを得る. 区間は0-indexed.
	Value1 get(int l, int r, int k = 1, int a = 0, int b = -1) {
		if (b == -1)b = N;

		if (l < 0 || r<0)assert(false);
		lazy_evaluate_node(k, a, b); // とりあえず辿ったノードは都合がいいので伝搬しとけ精神.

		if (b <= l || r <= a) //[a,b)と[l,r)が交差している場合
			return Zero1;

		if (l <= a && b <= r) { // [l,r)が[a,b)を完全に含んでいる場合
			return dat[k].sum;
		}

		int m = (a + b) / 2;
		Value1 vl = get(l, r, 2 * k, a, m);
		Value1 vr = get(l, r, 2 * k + 1, m, b);
		update_node(k);
		return connect(vl, vr);
	}
};

long long int dfs(const long long int num,vector<int>&ls,vector<int>&rs,int &a,vector<int>&sums,const vector<int>&nums,
	const vector<vector<int>>&edges, const int now, const int from) {
	ls[now]=a;
	sums[now]=num+nums[now];
	for (auto e : edges[now]) {
		if(e==from)continue;
		else {
			dfs(num+nums[now],ls,rs,a,sums,nums,edges,e,now);
		}
	}

	a++;
	rs[now]=a;
	return sums[now];
}


class Tree {
public:
	Tree(int V, int root) : V(V), root(root), cnum(V), place(V), id(V) {
		T.resize(V);
		for (int i = 0; i < MAXLOGV; i++) {
			parent[i].resize(V);
		}
		depth.resize(V);
	}
	// uとvをつなぐ
	// lcaを求めることが主目的なので無向グラフとしている
	void unite(int u, int v) {
		T[u].push_back(v);
		T[v].push_back(u);
	}
	void unite(vector<vector<int>>&e) {
		T = e;
	}
	// initする
	// コンストラクタだけじゃなくてこれも呼ばないとlcaが求められないぞ
	void init() {
		dfs(root, 0, 0);
		int id = 0;
		getid(root, 0, id);
	}
	// uとvのlcaを求める
	int lca(int u, int v) const {
		if (depth[u] > depth[v]) swap(u, v);
		for (int k = 0; k < MAXLOGV; k++) {
			if ((depth[v] - depth[u]) >> k & 1) {
				v = parent[k][v];
			}
		}
		if (u == v) return u;
		for (int k = MAXLOGV - 1; k >= 0; k--) {
			if (parent[k][u] != parent[k][v]) {
				u = parent[k][u];
				v = parent[k][v];
			}
		}
		return parent[0][u];
	}
	// uとvの距離を求める
	// edgeを定義しないといけない時はこれじゃダメ
	int dist(int u, int v) const {
		int p = lca(u, v);
		return (depth[u] - depth[p]) + (depth[v] - depth[p]);
	}
	int dfs(int v, int p, int d) {
		parent[0][v] = p;
		depth[v] = d;
		cnum[v] = 0;
		for (int i = 1; i < MAXLOGV; i++) {
			parent[i][v] = parent[i - 1][parent[i - 1][v]];
		}
		for (int next : T[v]) {
			if (next != p) cnum[v] += dfs(next, v, d + 1);
		}
		return cnum[v] + 1;
	}

	void dfs2(int v, int p, vector<vector<int>>&doubles, const vector<int>&nums) {
		for (int next : T[v]) {
			if (next != p) dfs2(next, v, doubles, nums);
		}
		doubles[0][v] = nums[v];
		for (int j = 1; j < MAXLOGV; ++j) {
			doubles[j][v] = min(doubles[j][v], doubles[j - 1][v]);
		}
		for (int j = 0; j < MAXLOGV - 1; ++j) {
			doubles[j + 1][parent[j][v]] = min(doubles[j + 1][parent[j][v]], doubles[j][v]);
		}
	}
	//ここでは親から距離2^iの部分木の最小値を求めている
	vector<vector<int>>get_doubles(const vector<int>&nums) {
		vector<vector<int>>doubles(MAXLOGV, vector<int>(V, 1e9));
		dfs2(root, -1, doubles, nums);
		return doubles;
	}

	void getid(const int v, const int p, int &nplace) {
		place[v] = nplace;
		id[nplace] = v;
		nplace++;
		for (int next : T[v]) {
			if (next != p) getid(next, v, nplace);
		}
	}
	static const int MAXLOGV = 25;
	// グラフの隣接リスト表現
	vector<vector<int> > T;
	// 頂点の数
	int V;
	// 根ノードの番号
	int root;

	// 親ノード
	vector<int> parent[MAXLOGV];
	// 根からの深さ
	vector<int> depth;

	//子の数
	vector<int>cnum;

	//変換
	vector<int>place;
	vector<int>id;

};

int main() {
	int N;cin>>N;
	lazy_segtree seg(N);
	vector<int>ls(N),rs(N),nums(N),sums(N);
	for (int i = 0; i < N; ++i) {
		int a;cin>>a;
		nums[i]=a;
	}
	vector<vector<int>>edges(N);
	for (int i = 0; i < N-1; ++i) {
		int a,b;cin>>a>>b;a--;b--;
		edges[a].push_back(b);
		edges[b].push_back(a);

	}
	int pp=0;
	dfs(0,ls,rs,pp,sums,nums,edges,0,-1);
	for (int i = 0; i < N; ++i) {
		seg.update(i,i+1,sums[i]);
	}
	Tree tree(N,0);
	tree.unite(edges);
	tree.init();
	int Q;cin>>Q;
	while (Q--) {
		int type;
		cin>>type;
		if (type == 0) {
			int a,b;cin>>a>>b;a--;b--;
			int ab_lca=tree.lca(a,b);
			long long int answer=seg.get(a,a+1)+seg.get(b,b+1)-
				seg.get(ab_lca,ab_lca+1);
			if (ab_lca != 0) {
				answer-=seg.get(tree.parent[0][ab_lca],tree.parent[0][ab_lca]+1);
			}
			cout<<answer<<endl;
		}
		else {
			int a,c;cin>>a>>c;a--;
			seg.update(ls[a],rs[a],c);
		}
	}
	return 0;
}
accuracy: 0.12903225806451613
time_ms: 160
memory_kb: 3416
final_rank: 15

code_snippet: #include "bits/stdc++.h"

#pragma warning(disable:4996)
using namespace std;
using ld = long double;
typedef long long int Value1;
typedef long long int Value2;
const Value1 Zero1(0);
const Value2 Zero2(0);

struct Node {
	Value1 sum;//更新された値. この値を参照する時は評価が完全に完了しているようにする.
	Value2 lazy;	//遅延されている値を保存している
	Node() :sum(Zero1) {
		lazy = Zero2;
	}
};
struct lazy_segtree {
	int N;
	vector<Node>dat;
	lazy_segtree(int n) :N(1) {
		while (N < n) N *= 2;
		dat.resize(2 * N);
	}

	Value2 lazy_connect(const Value2 l, const Value2 r) {
		return l + r;
	}

	void lazy_func(const int k, const int a, const int b) {

		dat[k].sum += dat[k].lazy*(b - a);

	}

	Value1 connect(const Value1 l, const Value1 r) {
		return l + r;

	}

	// inlineつけないと大変なことになるよ!(遅い)
	inline void lazy_evaluate_node(int k, int a, int b) {
		lazy_func(k, a, b);
		if (k < N) { // 2*k(左の子番号) < 2*N (節点の数) のイメージで. 末端ノードじゃなきゃ伝搬するのと等価.
			dat[2 * k].lazy = lazy_connect(dat[2 * k].lazy, dat[k].lazy);	//次は君が伝搬してね☆って感じ.
			dat[2 * k + 1].lazy = lazy_connect(dat[2 * k + 1].lazy, dat[k].lazy);
		}
		dat[k].lazy = Zero2;
	}

	inline void update_node(int k) { // kの子が既に評価されていることが前提. 末端以外のときしか呼び出さないような位置に書くのでif文要らない.
		dat[k].sum = connect(dat[2 * k].sum, dat[2 * k + 1].sum);

	}

	// update(l,r,v) := [l,r)を更新する. 区間は0-indexed.
	void update(int l, int r, Value2 v, int k = 1, int a = 0, int b = -1) {
		if (b == -1)b = N;
		if (l < 0 || r < 0)assert(false);
		lazy_evaluate_node(k, a, b); 	// とりあえず辿ったノードは都合がいいので伝搬しとけ精神.

		if (b <= l || r <= a) //[a,b)と[l,r)が交差している場合
			return;
		if (l <= a && b <= r) { // [l,r)が[a,b)を完全に含んでいる場合
			dat[k].lazy = lazy_connect(dat[k].lazy, v);
			lazy_evaluate_node(k, a, b); //一回遅延評価しとかないと都合悪いので.
			return;
		}

		int m = (a + b) / 2;
		update(l, r, v, 2 * k, a, m);
		update(l, r, v, 2 * k + 1, m, b);
		update_node(k);
	}
	//get(l,r) := [l,r)に対するクエリの答えを得る. 区間は0-indexed.
	Value1 get(int l, int r, int k = 1, int a = 0, int b = -1) {
		if (b == -1)b = N;

		if (l < 0 || r<0)assert(false);
		lazy_evaluate_node(k, a, b); // とりあえず辿ったノードは都合がいいので伝搬しとけ精神.

		if (b <= l || r <= a) //[a,b)と[l,r)が交差している場合
			return Zero1;

		if (l <= a && b <= r) { // [l,r)が[a,b)を完全に含んでいる場合
			return dat[k].sum;
		}

		int m = (a + b) / 2;
		Value1 vl = get(l, r, 2 * k, a, m);
		Value1 vr = get(l, r, 2 * k + 1, m, b);
		update_node(k);
		return connect(vl, vr);
	}
};

long long int dfs(const long long int num,vector<int>&ls,vector<int>&rs,int &a,vector<int>&sums,const vector<int>&nums,
	const vector<vector<int>>&edges, const int now, const int from) {
	ls[now]=a;
	sums[now]=num+nums[now];
	for (auto e : edges[now]) {
		if(e==from)continue;
		else {
			dfs(num+nums[now],ls,rs,a,sums,nums,edges,e,now);
		}
	}

	a++;
	rs[now]=a;
	return sums[now];
}


class Tree {
public:
	Tree(int V, int root) : V(V), root(root), cnum(V), place(V), id(V) {
		T.resize(V);
		for (int i = 0; i < MAXLOGV; i++) {
			parent[i].resize(V);
		}
		depth.resize(V);
	}
	// uとvをつなぐ
	// lcaを求めることが主目的なので無向グラフとしている
	void unite(int u, int v) {
		T[u].push_back(v);
		T[v].push_back(u);
	}
	void unite(vector<vector<int>>&e) {
		T = e;
	}
	// initする
	// コンストラクタだけじゃなくてこれも呼ばないとlcaが求められないぞ
	void init() {
		dfs(root, 0, 0);
		int id = 0;
		getid(root, 0, id);
	}
	// uとvのlcaを求める
	int lca(int u, int v) const {
		if (depth[u] > depth[v]) swap(u, v);
		for (int k = 0; k < MAXLOGV; k++) {
			if ((depth[v] - depth[u]) >> k & 1) {
				v = parent[k][v];
			}
		}
		if (u == v) return u;
		for (int k = MAXLOGV - 1; k >= 0; k--) {
			if (parent[k][u] != parent[k][v]) {
				u = parent[k][u];
				v = parent[k][v];
			}
		}
		return parent[0][u];
	}
	// uとvの距離を求める
	// edgeを定義しないといけない時はこれじゃダメ
	int dist(int u, int v) const {
		int p = lca(u, v);
		return (depth[u] - depth[p]) + (depth[v] - depth[p]);
	}
	int dfs(int v, int p, int d) {
		parent[0][v] = p;
		depth[v] = d;
		cnum[v] = 0;
		for (int i = 1; i < MAXLOGV; i++) {
			parent[i][v] = parent[i - 1][parent[i - 1][v]];
		}
		for (int next : T[v]) {
			if (next != p) cnum[v] += dfs(next, v, d + 1);
		}
		return cnum[v] + 1;
	}

	void dfs2(int v, int p, vector<vector<int>>&doubles, const vector<int>&nums) {
		for (int next : T[v]) {
			if (next != p) dfs2(next, v, doubles, nums);
		}
		doubles[0][v] = nums[v];
		for (int j = 1; j < MAXLOGV; ++j) {
			doubles[j][v] = min(doubles[j][v], doubles[j - 1][v]);
		}
		for (int j = 0; j < MAXLOGV - 1; ++j) {
			doubles[j + 1][parent[j][v]] = min(doubles[j + 1][parent[j][v]], doubles[j][v]);
		}
	}
	//ここでは親から距離2^iの部分木の最小値を求めている
	vector<vector<int>>get_doubles(const vector<int>&nums) {
		vector<vector<int>>doubles(MAXLOGV, vector<int>(V, 1e9));
		dfs2(root, -1, doubles, nums);
		return doubles;
	}

	void getid(const int v, const int p, int &nplace) {
		place[v] = nplace;
		id[nplace] = v;
		nplace++;
		for (int next : T[v]) {
			if (next != p) getid(next, v, nplace);
		}
	}
	static const int MAXLOGV = 25;
	// グラフの隣接リスト表現
	vector<vector<int> > T;
	// 頂点の数
	int V;
	// 根ノードの番号
	int root;

	// 親ノード
	vector<int> parent[MAXLOGV];
	// 根からの深さ
	vector<int> depth;

	//子の数
	vector<int>cnum;

	//変換
	vector<int>place;
	vector<int>id;

};

int main() {
	int N;cin>>N;
	lazy_segtree seg(N);
	vector<int>ls(N),rs(N),nums(N),sums(N);
	for (int i = 0; i < N; ++i) {
		int a;cin>>a;
		nums[i]=a;
	}
	vector<vector<int>>edges(N);
	for (int i = 0; i < N-1; ++i) {
		int a,b;cin>>a>>b;a--;b--;
		edges[a].push_back(b);
		edges[b].push_back(a);

	}
	int pp=0;
	dfs(0,ls,rs,pp,sums,nums,edges,0,-1);
	for (int i = 0; i < N; ++i) {
		seg.update(i,i+1,sums[i]);
	}
	Tree tree(N,0);
	tree.unite(edges);
	tree.init();
	int Q;cin>>Q;
	while (Q--) {
		int type;
		cin>>type;
		if (type == 0) {
			int a,b;cin>>a>>b;a--;b--;
			int ab_lca=tree.lca(a,b);
			long long int answer=seg.get(a,a+1)+seg.get(b,b+1)-
				seg.get(ab_lca,ab_lca+1);
			cout<<answer<<endl;
		}
		else {
			int a,c;cin>>a>>c;a--;
			seg.update(ls[a],rs[a],c);
		}
	}
	return 0;
}
accuracy: 0.12903225806451613
time_ms: 160
memory_kb: 3428
final_rank: 16

code_snippet: #include <iostream>
#include <algorithm>
#include <vector>
#include <memory>
using namespace std;

typedef long long ll;

struct BIT {
  int n;
  vector<ll> data;
  BIT(int size) : n(size), data(size+1, 0LL) { }
  void add(int k, ll a) {
    k++;
    while(k <= n) { data[k] += a; k += (k & -k); }
  }
  // [0, i) の和(半開区間に注意)
  ll sum(int k) {
    ll s = 0;
    while(k > 0) { s += data[k]; k -= (k & -k); }
    return s;
  }
};

struct LCA {
  int N, logN; // logN == ceil(lg(N))
  vector<vector<int>> G;
  vector<int> depth;
  // vから根の方向に2^k回登ったときのノード parent[k][v]
  vector<vector<int>> parent;

  LCA(int size) : N(size), G(size), depth(size) {
    logN = 0;
    for(int x = 1; x < N; x *= 2) logN++;
    parent.assign(max(logN, 1), vector<int>(N));
  }
  // どちらが根であってもOK
  void add_edge(int u, int v) {
    G[u].push_back(v);
    G[v].push_back(u);
  }
  void dfs(int v, int par, int dep) {
    depth[v] = dep;
    parent[0][v] = par;
    for(int next : G[v]) {
      if(next != par) dfs(next, v, dep + 1);
    }
  }
  void init(int root) {
    dfs(root, -1, 0);
    for(int k = 1; k < logN; k++) {
      for(int v = 0; v < N; v++) {
        if(parent[k - 1][v] == -1) parent[k][v] = -1;
        else parent[k][v] = parent[k - 1][parent[k - 1][v]];
      }
    }
  }
  int lca(int u, int v) {
    if(depth[u] > depth[v]) swap(u, v); // vのほうが深くなる
    // uとvの深さが同じになるまでvを根の方向に移動する
    for(int k = 0; k < logN; k++) {
      if(((depth[v] - depth[u]) >> k) & 1) {
        v = parent[k][v];
      }
    }
    if(u == v) return u;
    for(int k = logN - 1; k >= 0; k--) {
      if(parent[k][u] != parent[k][v]) {
        u = parent[k][u];
        v = parent[k][v];
      }
    }
    return parent[0][u];
  }
};

int dfs(int v, int parent, const vector<vector<int>> &G, vector<int> &child, vector<int> &order, int &k) {
  order[v] = k++;
  int cnt = 0;
  for(int next : G[v]) {
    if(next == parent) continue;
    cnt += dfs(next, v, G, child, order, k);
  }
  return child[v] = cnt + 1;
}

shared_ptr<BIT> bit0, bit1;

void add(int l, int r, ll x) {
  if(r < l) swap(r, l);
  bit0->add(l, -x * l);
  bit1->add(l, x);
  bit0->add(r, x * r);
  bit1->add(r, -x);
}

ll sum(int l, int r) {
  return bit0->sum(r) + r*bit1->sum(r) - bit0->sum(l) - l*bit1->sum(l);
}

int main() {
  int N; cin >> N;
  vector<int> COST(N);
  for(int i = 0; i < N; ++i) {
    cin >> COST[i];
  }
  LCA lca(N);
  vector<vector<int>> TREE(N);
  for(int i = 0; i < N - 1; ++i) {
    int u, v; cin >> u >> v;
    --u; --v;
    TREE[u].push_back(v);
    TREE[v].push_back(u);
    lca.add_edge(u, v);
  }
  lca.init(0);
  vector<int> child(N); // 子の数
  vector<int> order(N); // dfs順
  int k = 0;
  dfs(0, -1, TREE, child, order, k);
  bit0 = make_shared<BIT>(N);
  bit1 = make_shared<BIT>(N);
  for(int i = 0; i < N; ++i) {
    add(order[i], order[i] + child[i], COST[i]);
  }
  int Q; cin >> Q;
  for(int i = 0; i < Q; ++i) {
    int type, from, to; cin >> type >> from >> to;
    if(type == 0) {
      --from; --to;
      int v = lca.lca(from, to);
      ll res = 0LL;
      res += sum(order[from], order[from] + 1);
      res += sum(order[to], order[to] + 1);
      res -= 2 * sum(0, order[v]);
      res -= sum(order[v], order[v] + 1);
      cout << res << endl;
    }
    else {
      --from;
      add(order[from], order[from] + child[from], to);
    }
  }
}
accuracy: 0.12903225806451613
time_ms: 160
memory_kb: 1492
final_rank: 13

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

#define dump(...) (cerr<<#__VA_ARGS__<<" = "<<(DUMP(),__VA_ARGS__).str()<<endl)
struct DUMP : stringstream {
    template<class T>
    DUMP &operator,(const T &t) {
        if(this->tellp()) *this << ", ";
        *this << t;
        return *this;
    }
};

class BIT {
private:
    int size;
    vector<long long> bit;

public:
    BIT(int n = 0):size(n), bit(n + 1, 0) {}

    void add(int i, long long x) {
        while(i <= size) {
            bit[i] += x;
            i += i & -i;
        }
    }

    long long sum(int i) const {
        long long s = 0;
        while(i > 0) {
            s += bit[i];
            i -= i & -i;
        }
        return s;
    }
};

const int MAX_N = 2000;
const int MAX_LOG_N = 12;

int n;
int parent[MAX_LOG_N][MAX_N];
int depth[MAX_N];

long long cost[MAX_N];
vector<int> edges[MAX_N];

int pos_plus[MAX_N];
int pos_minus[MAX_N];

BIT bit(MAX_N * 2);

void dfs(int v, int p, int d, int &index) {
    parent[0][v] = p;
    depth[v] = d;

    pos_plus[v] = index;
    bit.add(index++, cost[v]);

    for(const auto &to : edges[v]) {
        if(to != p) dfs(to, v, d + 1, index);
    }

    pos_minus[v] = index;
    bit.add(index++, -cost[v]);
}

void calc() {
    int index = 1;
    dfs(0, -1, 0, index);
	assert(index == 2 * n + 1);
    for(int k = 0; k < MAX_LOG_N - 1; ++k) {
        for(int v = 0; v < n; ++v) {
            parent[k + 1][v] = (parent[k][v] == -1 ? -1 : parent[k][parent[k][v]]);
        }
    }
}

int lca(int u, int v) {
    if(depth[u] > depth[v]) swap(u, v);
    for(int k = 0; k < MAX_LOG_N; ++k) {
        if((depth[v] - depth[u]) >> k & 1) v = parent[k][v];
    }
    if(u == v) return u;
    for(int k = MAX_LOG_N - 1; k >= 0; --k) {
        if(parent[k][u] != parent[k][v]) {
            u = parent[k][u];
            v = parent[k][v];
        }
    }
    return parent[0][u];
}

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

    cin >> n;

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

    for(int i = 0; i < n - 1; ++i) {
        int a, b;
        cin >> a >> b;
        --a; --b;
        edges[a].emplace_back(b);
        edges[b].emplace_back(a);
    }

    calc();

    int q;
    cin >> q;
	while(q--) {
        int x;
        cin >> x;

        if(x) {
            int a;
			long long c;
            cin >> a >> c;
            --a;
            cost[a] += c;
            bit.add(pos_plus[a], c);
            bit.add(pos_minus[a], -c);
        }
        else {
            int a, b;
            cin >> a >> b;
            --a; --b;
            if(pos_plus[a] > pos_plus[b]) swap(a, b);
            const int l = lca(a, b);
            cout << (bit.sum(pos_plus[a]) - bit.sum(pos_plus[l])) - (bit.sum(pos_minus[l]) - bit.sum(pos_minus[b] - 1)) << endl;
        }
    }

    return 0;
}
accuracy: 1
time_ms: 1210
memory_kb: 1648
final_rank: 6

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

#define dump(...) (cerr<<#__VA_ARGS__<<" = "<<(DUMP(),__VA_ARGS__).str()<<endl)
struct DUMP : stringstream {
    template<class T>
    DUMP &operator,(const T &t) {
        if(this->tellp()) *this << ", ";
        *this << t;
        return *this;
    }
};

class BIT {
private:
    int size;
    vector<long long> bit;

public:
    BIT(int n = 0):size(n), bit(n + 1, 0) {}

    void add(int i, long long x) {
        while(i <= size) {
            bit[i] += x;
            i += i & -i;
        }
    }

    long long sum(int i) const {
        long long s = 0;
        while(i > 0) {
            s += bit[i];
            i -= i & -i;
        }
        return s;
    }
};

const int MAX_N = 2000;
const int MAX_LOG_N = 12;

int n;
int parent[MAX_LOG_N][MAX_N];
int depth[MAX_N];

long long cost[MAX_N];
vector<int> edges[MAX_N];

int pos_plus[MAX_N];
int pos_minus[MAX_N];

BIT bit(MAX_N * 2);

void dfs(int v, int p, int &index) {
    parent[0][v] = p;
    depth[v] = (p == -1 ? 0 : depth[p] + 1);

    pos_plus[v] = index;
    bit.add(index++, cost[v]);

    for(const auto &to : edges[v]) {
        if(to != p) dfs(to, v, index);
    }

    pos_minus[v] = index;
    bit.add(index++, -cost[v]);
}

void calc() {
    for(int k = 0; k < MAX_LOG_N - 1; ++k) {
        for(int v = 0; v < n; ++v) {
            parent[k + 1][v] = (parent[k][v] == -1 ? -1 : parent[k][parent[k][v]]);
        }
    }
}

int lca(int u, int v) {
    if(depth[u] > depth[v]) swap(u, v);
    for(int k = 0; k < MAX_LOG_N; ++k) {
        if((depth[v] - depth[u]) & (k << 1)) v = parent[k][v];
    }
    if(u == v) return u;
    for(int k = MAX_LOG_N - 1; k >= 0; --k) {
        if(parent[k][u] != parent[k][v]) {
            u = parent[k][u];
            v = parent[k][v];
        }
    }
    return parent[0][u];
}

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

    cin >> n;

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

    for(int i = 0; i < n - 1; ++i) {
        int a, b;
        cin >> a >> b;
        --a; --b;
        edges[a].emplace_back(b);
        edges[b].emplace_back(a);
    }

    memset(parent, -1, sizeof(parent));
    int index = 1;
    dfs(0, -1, index);
    calc();

    int q;
    cin >> q;
    for(int i = 0; i < q; ++i) {
        int x;
        cin >> x;

        if(x) {
            int a;
			long long c;
            cin >> a >> c;
            --a;
            cost[a] += c;
            bit.add(pos_plus[a], c);
            bit.add(pos_minus[a], -c);
        }
        else {
            int a, b;
            cin >> a >> b;
            --a; --b;
            if(pos_plus[a] > pos_plus[b]) swap(a, b);
            const int l = lca(a, b);
            cout << (bit.sum(pos_plus[a]) - bit.sum(pos_plus[l] - 1)) - (bit.sum(pos_minus[l]) - bit.sum(pos_minus[b] - 1)) - cost[l] << endl;
        }
    }

    return 0;
}
accuracy: 0.12903225806451613
time_ms: 120
memory_kb: 1516
final_rank: 12

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

#define dump(...) (cerr<<#__VA_ARGS__<<" = "<<(DUMP(),__VA_ARGS__).str()<<endl)
struct DUMP : stringstream {
	template<class T>
	DUMP &operator,(const T &t) {
		if(this->tellp()) *this << ", ";
		*this << t;
		return *this;
	}
};

class BIT {
private:
	int size;
	vector<int> bit;
	
public:
	BIT(int n = 0):size(n), bit(n + 1, 0) {}
	
	void add(int i, int x) {
		while(i <= size) {
			bit[i] += x;
			i += i & -i;
		}
	}

	int sum(int i) const {
		int s = 0;
		while(i > 0) {
			s += bit[i];
			i -= i & -i;
		}
		return s;
	}
};

const int MAX_N = 2000;
const int MAX_LOG_N = 12;

int n;
int parent[MAX_LOG_N][MAX_N];
int depth[MAX_N];

int cost[MAX_N];
vector<int> edges[MAX_N];

int pos_plus[MAX_N];
int pos_minus[MAX_N];

BIT bit(MAX_N * 2);

void dfs(int v, int p, int &index) {
	parent[0][v] = p;
	depth[v] = (p == -1 ? 0 : depth[p] + 1);

	pos_plus[v] = index;
	bit.add(index++, cost[v]);

	for(const auto &to : edges[v]) {
		if(to != p) dfs(to, v, index);
	}

	pos_minus[v] = index;
	bit.add(index++, -cost[v]);
}

void calc() {
	for(int k = 0; k < MAX_LOG_N - 1; ++k) {
		for(int v = 0; v < n; ++v) {
			parent[k + 1][v] = (parent[k][v] == -1 ? -1 : parent[k][parent[k][v]]);
		}
	}
}

int lca(int u, int v) {
	if(depth[u] > depth[v]) swap(u, v);
	for(int k = 0; k < MAX_LOG_N; ++k) {
		if((depth[v] - depth[u]) & (k << 1)) v = parent[k][v];
	}
	if(u == v) return u;
	for(int k = MAX_LOG_N - 1; k >= 0; --k) {
		if(parent[k][u] != parent[k][v]) {
			u = parent[k][u];
			v = parent[k][v];
		}
	}
	return parent[0][u];
}

int main() {
	cin >> n;

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

	for(int i = 0; i < n - 1; ++i) {
		int a, b;
		cin >> a >> b;
		--a; --b;
		edges[a].emplace_back(b);
		edges[b].emplace_back(a);
	}

	memset(parent, -1, sizeof(parent));
	int index = 1;
	dfs(0, -1, index);
	calc();
	
	int q;
	cin >> q;
	for(int i = 0; i < q; ++i) {
		int x;
		cin >> x;

		if(x) {
			int a, c;
			cin >> a >> c;
			--a;
			cost[a] += c;
			bit.add(pos_plus[a], c);
			bit.add(pos_minus[a], -c);
		}
		else {
			int a, b;
			cin >> a >> b;
			--a; --b;
			if(pos_plus[a] > pos_plus[b]) swap(a, b);
			const int l = lca(a, b);
			cout << (bit.sum(pos_plus[a]) - bit.sum(pos_plus[l] - 1)) - (bit.sum(pos_minus[l]) - bit.sum(pos_minus[b] - 1)) - cost[l] << endl;
		}
	}

	return 0;
}
accuracy: 0.12903225806451613
time_ms: 170
memory_kb: 1488
final_rank: 14

code_snippet: #include <iostream>
#include <stack>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
 
#define INF (1145141919)
struct Seg{
    long long lazy;
    long long sum;
    long long ml,mm,mr;
    long long maximum;
    Seg(){
        lazy = INF;
        sum = 0;
        ml = mm = mr = 0;
        maximum = 0;
    }
};
inline void output(vector<Seg> &seg,int k,int a,int b){
    cout << "[k:" << k << ", a:" << a << ", b:" << b << "]" << " ";
    cout << "sum:" << seg[k].sum << ", ml:" << seg[k].ml << ", mm:" << seg[k].mm << ", mr:" << seg[k].mr << ", maximum:" << seg[k].maximum << endl;
}
 
 
inline void lazy(vector<Seg> &seg,int lazyval,int k,int a,int b){
    seg[k].lazy = lazyval;
    seg[k].sum = seg[k].lazy * (b-a+1);
    seg[k].mm = seg[k].ml = seg[k].mr = max(seg[k].sum,0ll);
    seg[k].maximum = lazyval;
}
 
inline void spread(vector<Seg> &seg,int k,int a,int b){
    if( seg[k].lazy == INF ) return;
    seg[k].sum = seg[k].lazy * (b-a+1);
    if( a != b ){
        int m = (a+b) / 2;
        lazy(seg,seg[k].lazy,k*2,a,m);
        lazy(seg,seg[k].lazy,k*2+1,m+1,b);
    }
    seg[k].lazy = INF;
}
 
 
inline void reflesh(vector<Seg> &seg,int k,int a,int b){
    if( a != b ){
        seg[k].sum = seg[k*2].sum + seg[k*2+1].sum;
        seg[k].ml = max(seg[k*2].ml,seg[k*2].sum + seg[k*2+1].ml);
        seg[k].mr = max(seg[k*2+1].mr,seg[k*2].mr + seg[k*2+1].sum);
        seg[k].mm = max( max( seg[k*2].mm ,  seg[k*2+1].mm ) , seg[k*2].mr + seg[k*2+1].ml );
        seg[k].maximum = max(seg[k*2].maximum,seg[k*2+1].maximum);
    }
}
 
 
int change(vector<Seg> &seg,int l,int r,int v,int k,int a,int b){
    spread(seg,k,a,b);
    if( r < a || b < l ){
        return 0;
    }
     
    if( l <= a && b <= r ){
        lazy(seg,v,k,a,b);
        return 0;
    }
    int m = (a+b) / 2;
    change(seg,l,r,v,k*2,a,m);
    change(seg,l,r,v,k*2+1,m+1,b);
    reflesh(seg,k,a,b);
}
 
 
struct Three{
    long long ml,mm,mr,sum,maximum;
    Three() : ml(0) , mm(0) , mr(0) , sum(0) {}
    Three(long long ml,long long mm,long long mr,long long sum,long long maximum) : ml(ml) , mm(mm) , mr(mr) , sum(sum) , maximum(maximum) {}
};
 
inline void output(Three x){
    //cout << "[k:" << k << ", a:" << a << ", b:" << b << "]" << " ";
    cout << "sum:" << x.sum << ", ml:" << x.ml << ", mm:" << x.mm << ", mr:" << x.mr << ", maximum:" << x.maximum << endl;
}
 
 
Three getMaximum(vector<Seg> &seg,int l,int r,int k,int a,int b){
    spread(seg,k,a,b);
    if( r < a || b < l ){
        return Three(0,0,0,0,-INF);
    }
    if( l <= a && b <= r ){
        //output(seg,k,a,b);
        return Three(seg[k].ml,seg[k].mm,seg[k].mr,seg[k].sum,seg[k].maximum);
    }
    int m = (a+b) / 2;
    Three le = getMaximum(seg,l,r,k*2,a,m);
    Three ri = getMaximum(seg,l,r,k*2+1,m+1,b);
    Three ans = Three( max(ri.ml+le.sum,le.ml) , max( max(le.mm,ri.mm) , le.mr+ri.ml ) , max(le.mr+ri.sum,ri.mr) , ri.sum + le.sum , max(le.maximum,ri.maximum) ); 
 
    //cout << "(" << a << "," << b << ")" << "[" << ans.ml << " " << ans.mm << " " << ans.mr << " " << ans.sum << "]" << endl;
    reflesh(seg,k,a,b);
    return ans;
}
 
typedef unsigned int uint;
int in() {
    int x = 0, c;
    for (; (uint)((c = getchar()) - '0') >= 10; ) { if (c == '-') return -in(); if (!~c) throw ~0; }
    do { x = (x << 3) + (x << 1) + (c - '0'); } while ((uint)((c = getchar()) - '0') < 10);
    return x;
}
 
int parent[19][200010];
int depth[200010];
vector<int> gr[200010];
int dfs(int x_,int p_){
    stack< pair<int,int> > S;
    S.push(make_pair(x_,p_));
    while(S.size()){
        int x = S.top().first;
        int p = S.top().second;
        S.pop();
        parent[0][x] = p;
        if( p != -1 ) depth[x] = depth[p] + 1;
        for(int i = 0 ; i < gr[x].size() ; i++)
            if( gr[x][i] != p ) S.push(make_pair(gr[x][i],x));
    }
}
 
void genlca(){
    for(int i = 1 ; i < 19 ; i++){
        for(int j = 0 ; j < 200010 ; j++){
            if( parent[i-1][j] != -1 )
                parent[i][j] = parent[i-1][parent[i-1][j]];
        }
    }
}
pair< int,pair<int,int> > lca(int x,int y){
    //if( depth[x] > depth[y] ) swap(x,y);
    int d = depth[y] - depth[x];
    for(int i = 0 ; i < 19 ; i++){
        if( d >> i & 1 ) y = parent[i][y];
    }
    if( x == y ) return make_pair(x,make_pair(-1,-1));
     
    //if( depth[x] != depth[y] ) cout << "^^" << endl;
    for(int i = 17 ; i >= 0 ; i--){
        if( parent[i][x] != parent[i][y] ){
            x = parent[i][x];
            y = parent[i][y];
        }
    }
    return make_pair( parent[0][x] , make_pair(x,y) );
}
 
int gNum = 0;
int belong[200010];
int height[200010];
int lightEdge[200010];
int heavyHeight[200010];
int size[200010];
vector<Seg> seg[200010];
 
int initval[200010];
 
int build(int x_,int where){
    stack< pair<int,int> > S;
    S.push( make_pair(x_,-1) );
    vector<int> tmp;
    while(S.size()){
        int x    = S.top().first;
        int prev = S.top().second;
        S.pop();
        if( x == -1 ) continue;
        if( prev != -1 && size[prev] * 2 < size[x] ){
            lightEdge[where] = x;
            continue;
        }
        if( belong[x] != -1 ){
            lightEdge[where] = x;
            continue;
        }
        tmp.push_back(x);
        belong[x] = where;
        S.push(make_pair(parent[0][x],x));
    }
    for(int i = 0 ; i < tmp.size() ; i++)
        height[tmp[i]] = tmp.size() - i;
    return tmp.size() + 1;
}
 
void query1(int st,int en,int val){
    if( belong[st] != belong[en] ){
        int s = belong[st];
        int a = height[st];
        change(seg[s],1,a,val,1,1,seg[s].size()/2);     
        query1(lightEdge[s],en,val);
    }else{
        int s = belong[st];
        int a = height[st];
        int b = height[en];
        change(seg[s],b,a,val,1,1,seg[s].size()/2);
    }
}
 
Three query2(int st,int en,Three ri){
    if( belong[st] != belong[en] ){
        int s = belong[st];
        int a = height[st];
        Three le = getMaximum(seg[s],1,a,1,1,seg[s].size()/2);
        Three ans = Three( max(ri.ml+le.sum,le.ml) , max( max(le.mm,ri.mm) , le.mr+ri.ml ) , max(le.mr+ri.sum,ri.mr) , ri.sum + le.sum , max(le.maximum,ri.maximum) ); 
        return query2(lightEdge[s],en,ans);
         
    }else{
        int s = belong[st];
        int a = height[st];
        int b = height[en];
        Three le = getMaximum(seg[s],b,a,1,1,seg[s].size()/2);
        Three ans = Three( max(ri.ml+le.sum,le.ml) , max( max(le.mm,ri.mm) , le.mr+ri.ml ) , max(le.mr+ri.sum,ri.mr) , ri.sum + le.sum , max(le.maximum,ri.maximum) ); 
        return ans;
    }
}
 
int main(){
     
    memset(parent,-1,sizeof(parent));
    memset(belong,-1,sizeof(belong));
    memset(lightEdge,-1,sizeof(lightEdge));
    int n = in();
    for(int i = 0 ; i < n ; i++) initval[i] = in();
    for(int i = 0 ; i < n-1 ; i++){
        int a = in();
        int b = in();
        a--,b--;
        gr[a].push_back(b);
        gr[b].push_back(a);
    }
    dfs(0,-1);
    genlca();
    vector< pair<int,int> > sorted;
    for(int i = 0 ; i < n ; i++){
        sorted.push_back(make_pair(-depth[i],i));
    }
    sort(sorted.begin(),sorted.end());
    for(int j = 0 ; j < n ; j++){
        int i = sorted[j].second;
        size[i]++;
        if( parent[0][i] != -1 ) size[parent[0][i]]+=size[i];
    }
    for(int j = 0 ; j < n ; j++){
        int i = sorted[j].second;
        if( belong[i] == -1 ){
            int sz = build(i,gNum) - 1;
            int sz_ = 1;
            while( sz_ < sz ) sz_ *= 2;
            seg[gNum].resize(2*sz_);
            gNum++;
        }
    }
    for(int i = 0 ; i < n ; i++){
        change(seg[belong[i]],height[i],height[i],initval[i],1,1,seg[belong[i]].size()/2);
    }
    int m = in();
    for(int i = 0 ; i < m ; i++){
        int t,a,b,c;
        t = in(); a = in(); b = a; c = in();
        if( t == 1 ){
        	a--,b--;
        if( depth[a] > depth[b] ) swap(a,b);
        pair<int,pair<int,int> > tmp = lca(a,b);
        int p = tmp.first;
        int pa = tmp.first;
        int pb = tmp.second.second;
        	initval[a] += c;
        	query1(a,b,initval[a]);
        }else{
        	a--;
        	b = c-1;
        if( depth[a] > depth[b] ) swap(a,b);
        pair<int,pair<int,int> > tmp = lca(a,b);
        int p = tmp.first;
        int pa = tmp.first;
        int pb = tmp.second.second;
            Three res,ri,le;
            if( p == a ){
                res = query2(b,p,Three(0,0,0,0,-INF));
            }else{
                le  = query2(a,p,Three(0,0,0,0,-INF));
                ri  = query2(b,pb,Three(0,0,0,0,-INF));
                res = Three( -1 , max( max(le.mm,ri.mm) , le.ml+ri.ml ) ,-1 , ri.sum+le.sum , max(ri.maximum,le.maximum) );
            }
            printf("%lld\n",res.sum);
        }
    }
}
accuracy: 1
time_ms: 520
memory_kb: 30408
final_rank: 8

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

#define rep(i,n) for(int i=0;i<(int)n;i++)
#define all(c) (c).begin(),(c).end()
#define mp make_pair
#define pb push_back
#define each(i,c) for(__typeof((c).begin()) i=(c).begin();i!=(c).end();i++)
#define dbg(x) cerr<<__LINE__<<": "<<#x<<" = "<<(x)<<endl

using namespace std;

typedef long long ll;
typedef vector<int> vi;
typedef pair<int,int> pi;
const int inf = (int)1e9;
const double INF = 1e12, EPS = 1e-9;

const int N = 2000;
const int M = 8192;

int n, a[N], b[N], cost[N];
vector<vi> e;

int parent[11][N];
int depth[N];

int euler[2*N], sz;
int pos[N][2];


int seg_sz = 1;
ll dat[2*M];
inline ll query(int a, int b, int k, int l, int r){
	
	if(b <= l || a >= r) return 0;
	if(a <= l && r <= b) return dat[k];
	
	ll lch = query(a, b, k * 2 + 1, l, (l + r) / 2);
	ll rch = query(a, b, k * 2 + 2, (l + r) / 2, r);
	
	return lch + rch;
}
inline void update(int pos, int d){
	
	pos += seg_sz - 1;
	dat[pos] += d;
	
	while(1){
		if(pos == 0) break;
		pos = (pos - 1) / 2;
		dat[pos] = dat[pos * 2 + 1] + dat[pos * 2 + 2];
	}
}

void dfs(int c, int p, int d = 1){
	
	depth[c] = d;
	parent[0][c] = p;
	dat[seg_sz - 1 + sz] = cost[c];
	euler[sz] = c;
	pos[c][0] = sz++;
	
	rep(i, e[c].size()){
		int id = e[c][i];
		int to = id < 0 ? a[-id - 1] : b[id - 1];
		
		if(to != p) dfs(to, c, d + 1);
	}
	
	dat[seg_sz - 1 + sz] = -cost[c];
	euler[sz] = c;
	pos[c][1] = sz++;
}

inline int lca(int a, int b){
	if(depth[a] < depth[b]) swap(a, b);
	
	for(int i = 10; i >= 0; i--) if(depth[a] - depth[b] >> i & 1) a = parent[i][a];
	
	if(a == b) return a;
	
	for(int i = 10; i >= 0; i--) if(parent[i][a] != parent[i][b]){
		
		a = parent[i][a];
		b = parent[i][b];
	}
	return parent[0][a];
}

int main(){
	
	scanf("%d", &n);
	e.resize(n);
	
	rep(i, n) scanf("%d", cost + i);
	rep(i, n - 1){
		scanf("%d%d", a + i, b + i);
		a[i]--; b[i]--;
		
		e[a[i]].pb(i + 1);
		e[b[i]].pb(-i - 1);
	}
	
	for(; seg_sz < 2 * n; seg_sz *= 2);
	
	dfs(0, 0);
	
	rep(i, 10) rep(j, n) parent[i + 1][j] = parent[i][parent[i][j]];
	
	for(int i = seg_sz - 2; i >= 0; i--){
		
		dat[i] = dat[i * 2 + 1] + dat[i * 2 + 2];
	}
	
	#if 0
	rep(i, seg_sz) cerr<<dat[seg_sz - 1 + i]<<" ";cerr<<endl;
	rep(i, n) cerr<<pos[i][0]<<" "<<pos[i][1]<<endl;
	cerr<<"---"<<endl;
	#endif
	
	int qs; scanf("%d", &qs);
	
	while(qs--){
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		
		if(x == 0){
			y--; z--;
			
			int p = lca(y, z);
			ll ans = 0;
			
			ans += query(0, pos[y][0] + 1, 0, 0, seg_sz);
			ans += query(0, pos[z][0] + 1, 0, 0, seg_sz);
			ans -= query(0, pos[p][0] + 1, 0, 0, seg_sz) * 2;
			ans += dat[seg_sz - 1 + pos[p][0]];
			
			printf("%lld\n", ans);
		}
		else{
			y--;
			update(pos[y][0], z);
			update(pos[y][1], -z);
		}
	}
	
	return 0;
}
accuracy: 1
time_ms: 570
memory_kb: 1660
final_rank: 4

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

#define rep(i,n) for(int i=0;i<(int)n;i++)
#define all(c) (c).begin(),(c).end()
#define mp make_pair
#define pb push_back
#define each(i,c) for(__typeof((c).begin()) i=(c).begin();i!=(c).end();i++)
#define dbg(x) cerr<<__LINE__<<": "<<#x<<" = "<<(x)<<endl

using namespace std;

typedef long long ll;
typedef vector<int> vi;
typedef pair<int,int> pi;
const int inf = (int)1e9;
const double INF = 1e12, EPS = 1e-9;

const int N = 2000;
const int M = 8192;

int n, a[N], b[N], cost[N];
vector<vi> e;

int parent[11][N];
int depth[N];

int euler[2*N], sz;
int pos[N][2];


int seg_sz = 1;
ll dat[2*M];
inline ll query(int a, int b, int k, int l, int r){
	
	if(b <= l || a >= r) return 0;
	if(a <= l && r <= b) return dat[k];
	
	ll lch = query(a, b, k * 2 + 1, l, (l + r) / 2);
	ll rch = query(a, b, k * 2 + 2, (l + r) / 2, r);
	
	return lch + rch;
}
inline void update(int pos, int d){
	
	pos += seg_sz - 1;
	dat[pos] += d;
	
	while(1){
		if(pos == 0) break;
		pos = (pos - 1) / 2;
		dat[pos] = dat[pos * 2 + 1] + dat[pos * 2 + 2];
	}
}

void dfs(int c, int p, int d = 1){
	
	depth[c] = d;
	parent[0][c] = p;
	dat[seg_sz - 1 + sz] = cost[c];
	euler[sz] = c;
	pos[c][0] = sz++;
	
	rep(i, e[c].size()){
		int id = e[c][i];
		int to = id < 0 ? a[-id - 1] : b[id - 1];
		
		if(to != p) dfs(to, c, d + 1);
	}
	
	dat[seg_sz - 1 + sz] = -cost[c];
	euler[sz] = c;
	pos[c][1] = sz++;
}

inline int lca(int a, int b){
	if(depth[a] < depth[b]) swap(a, b);
	
	for(int i = 10; i >= 0; i--) if(depth[a] - depth[b] >> i & 1) a = parent[i][a];
	
	if(a == b) return a;
	
	for(int i = 10; i >= 0; i--) if(parent[i][a] != parent[i][b]){
		
		a = parent[i][a];
		b = parent[i][b];
	}
	return parent[0][a];
}

int main(){
	
	scanf("%d", &n);
	e.resize(n);
	
	rep(i, n) scanf("%d", cost + i);
	rep(i, n - 1){
		scanf("%d%d", a + i, b + i);
		a[i]--; b[i]--;
		
		e[a[i]].pb(i + 1);
		e[b[i]].pb(-i - 1);
	}
	
	for(; seg_sz < 2 * n; seg_sz *= 2);
	
	dfs(0, 0);
	
	rep(i, 10) rep(j, n) parent[i + 1][j] = parent[i][parent[i][j]];
	
	for(int i = seg_sz - 2; i >= 0; i--){
		
		dat[i] = dat[i * 2 + 1] + dat[i * 2 + 2];
	}
	
	#if 0
	rep(i, seg_sz) cerr<<dat[seg_sz - 1 + i]<<" ";cerr<<endl;
	rep(i, n) cerr<<pos[i][0]<<" "<<pos[i][1]<<endl;
	cerr<<"---"<<endl;
	#endif
	
	int qs; scanf("%d", &qs);
	
	while(qs--){
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		
		if(x == 0){
			y--; z--;
			
			int p = lca(y, z);
			ll ans = 0;
			
			ans += query(0, pos[y][0] + 1, 0, 0, seg_sz);
			ans += query(0, pos[z][0] + 1, 0, 0, seg_sz);
			ans -= 2 * query(0, pos[p][0] + 1, 0, 0, seg_sz);
			ans += dat[seg_sz - 1 + p];
			
			printf("%lld\n", ans);
		}
		else{
			y--;
			update(pos[y][0], z);
			update(pos[y][1], -z);
		}
	}
	
	return 0;
}
accuracy: 0.12903225806451613
time_ms: 50
memory_kb: 1504
final_rank: 10

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

#define rep(i,n) for(int i=0;i<(int)n;i++)
#define all(c) (c).begin(),(c).end()
#define mp make_pair
#define pb push_back
#define each(i,c) for(__typeof((c).begin()) i=(c).begin();i!=(c).end();i++)
#define dbg(x) cerr<<__LINE__<<": "<<#x<<" = "<<(x)<<endl

using namespace std;

typedef long long ll;
typedef vector<int> vi;
typedef pair<int,int> pi;
const int inf = (int)1e9;
const double INF = 1e12, EPS = 1e-9;

const int N = 2000;
const int M = 8192;

int n, a[N], b[N], cost[N];
vector<vi> e;

int parent[11][N];
int depth[N];

int euler[2*N], sz;
int pos[N][2];


int seg_sz = 1;
ll dat[2*M];
inline ll query(int a, int b, int k, int l, int r){
	
	if(b <= l || a >= r) return 0;
	if(a <= l && r <= b) return dat[k];
	
	ll lch = query(a, b, k * 2 + 1, l, (l + r) / 2);
	ll rch = query(a, b, k * 2 + 2, (l + r) / 2, r);
	
	return lch + rch;
}
inline void update(int pos, int d){
	
	pos += seg_sz - 1;
	dat[pos] += d;
	
	while(1){
		if(pos == 0) break;
		pos = (pos - 1) / 2;
		dat[pos] = dat[pos * 2 + 1] + dat[pos * 2 + 2];
	}
}

void dfs(int c, int p, int d = 1){
	
	depth[c] = d;
	parent[0][c] = p;
	dat[seg_sz - 1 + sz] = cost[c];
	euler[sz] = c;
	pos[c][0] = sz++;
	
	rep(i, e[c].size()){
		int id = e[c][i];
		int to = id < 0 ? a[-id - 1] : b[id - 1];
		
		if(to != p) dfs(to, c, d + 1);
	}
	
	dat[seg_sz - 1 + sz] = -cost[c];
	euler[sz] = c;
	pos[c][1] = sz++;
}

inline int lca(int a, int b){
	if(depth[a] < depth[b]) swap(a, b);
	
	for(int i = 10; i >= 0; i--) if(depth[a] - depth[b] >> i & 1) a = parent[i][a];
	
	if(a == b) return a;
	
	for(int i = 10; i >= 0; i--) if(parent[i][a] != parent[i][b]){
		
		a = parent[i][a];
		b = parent[i][b];
	}
	return parent[0][a];
}

int main(){
	
	scanf("%d", &n);
	e.resize(n);
	
	rep(i, n) scanf("%d", cost + i);
	rep(i, n - 1){
		scanf("%d%d", a + i, b + i);
		a[i]--; b[i]--;
		
		e[a[i]].pb(i + 1);
		e[b[i]].pb(-i - 1);
	}
	
	for(; seg_sz < 2 * n; seg_sz *= 2);
	
	dfs(0, 0);
	
	rep(i, 10) rep(j, n) parent[i + 1][j] = parent[i][parent[i][j]];
	
	for(int i = seg_sz - 2; i >= 0; i--){
		
		dat[i] = dat[i * 2 + 1] + dat[i * 2 + 2];
	}
	
	#if 0
	rep(i, seg_sz) cerr<<dat[seg_sz - 1 + i]<<" ";cerr<<endl;
	rep(i, n) cerr<<pos[i][0]<<" "<<pos[i][1]<<endl;
	cerr<<"---"<<endl;
	#endif
	
	int qs; scanf("%d", &qs);
	
	while(qs--){
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		
		if(x == 0){
			y--; z--;
			
			int p = lca(y, z);
			ll ans = 0;
			
			ans += query(0, pos[y][0] + 1, 0, 0, seg_sz);
			ans += query(0, pos[z][0] + 1, 0, 0, seg_sz);
			ans -= query(0, pos[p][0] + 1, 0, 0, seg_sz);
			
			printf("%lld\n", ans);
		}
		else{
			y--;
			update(pos[y][0], z);
			update(pos[y][1], -z);
		}
	}
	
	return 0;
}
accuracy: 0.12903225806451613
time_ms: 50
memory_kb: 1504
final_rank: 10

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

#define rep(i,n) for(int i=0;i<(int)n;i++)
#define all(c) (c).begin(),(c).end()
#define mp make_pair
#define pb push_back
#define each(i,c) for(__typeof((c).begin()) i=(c).begin();i!=(c).end();i++)
#define dbg(x) cerr<<__LINE__<<": "<<#x<<" = "<<(x)<<endl

using namespace std;

typedef long long ll;
typedef vector<int> vi;
typedef pair<int,int> pi;
const int inf = (int)1e9;
const double INF = 1e12, EPS = 1e-9;

const int N = 2000;
const int M = 8192;

int n, a[N], b[N], cost[N];
vector<vi> e;

int parent[11][N];
int depth[N];

int euler[2*N], sz;
int pos[N][2];


int seg_sz = 1;
int dat[2*M];
inline int query(int a, int b, int k, int l, int r){
	
	if(b <= l || a >= r) return 0;
	if(a <= l && r <= b) return dat[k];
	
	int lch = query(a, b, k * 2 + 1, l, (l + r) / 2);
	int rch = query(a, b, k * 2 + 2, (l + r) / 2, r);
	
	return lch + rch;
}
inline void update(int pos, int d){
	
	pos += seg_sz - 1;
	dat[pos] += d;
	
	while(1){
		if(pos == 0) break;
		pos = (pos - 1) / 2;
		dat[pos] = dat[pos * 2 + 1] + dat[pos * 2 + 2];
	}
}

void dfs(int c, int p, int d = 1){
	
	depth[c] = d;
	parent[0][c] = p;
	dat[seg_sz - 1 + sz] = cost[c];
	euler[sz] = c;
	pos[c][0] = sz++;
	
	rep(i, e[c].size()){
		int id = e[c][i];
		int to = id < 0 ? a[-id - 1] : b[id - 1];
		
		if(to != p) dfs(to, c, d + 1);
	}
	
	dat[seg_sz - 1 + sz] = -cost[c];
	euler[sz] = c;
	pos[c][1] = sz++;
}

inline int lca(int a, int b){
	if(depth[a] < depth[b]) swap(a, b);
	
	for(int i = 10; i >= 0; i--) if(depth[a] - depth[b] >> i & 1) a = parent[i][a];
	
	if(a == b) return a;
	
	for(int i = 10; i >= 0; i--) if(parent[i][a] != parent[i][b]){
		
		a = parent[i][a];
		b = parent[i][b];
	}
	return parent[0][a];
}

int main(){
	
	scanf("%d", &n);
	e.resize(n);
	
	rep(i, n) scanf("%d", cost + i);
	rep(i, n - 1){
		scanf("%d%d", a + i, b + i);
		a[i]--; b[i]--;
		
		e[a[i]].pb(i + 1);
		e[b[i]].pb(-i - 1);
	}
	
	for(; seg_sz < 2 * n; seg_sz *= 2);
	
	dfs(0, 0);
	
	rep(i, 10) rep(j, n) parent[i + 1][j] = parent[i][parent[i][j]];
	
	for(int i = seg_sz - 2; i >= 0; i--){
		
		dat[i] = dat[i * 2 + 1] + dat[i * 2 + 2];
	}
	
	#if 0
	rep(i, seg_sz) cerr<<dat[seg_sz - 1 + i]<<" ";cerr<<endl;
	rep(i, n) cerr<<pos[i][0]<<" "<<pos[i][1]<<endl;
	cerr<<"---"<<endl;
	#endif
	
	int qs; scanf("%d", &qs);
	
	while(qs--){
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		
		if(x == 0){
			y--; z--;
			
			int p = lca(y, z);
			ll ans = 0;
			
			ans += query(0, pos[y][0] + 1, 0, 0, seg_sz);
			ans += query(0, pos[z][0] + 1, 0, 0, seg_sz);
			ans -= query(0, pos[p][0] + 1, 0, 0, seg_sz);
			
			printf("%lld\n", ans);
		}
		else{
			y--;
			update(pos[y][0], z);
			update(pos[y][1], -z);
		}
	}
	
	return 0;
}
accuracy: 0.12903225806451613
time_ms: 50
memory_kb: 1488
final_rank: 9

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <string>
#include <vector>
#include <algorithm>
#include <numeric>
#include <set>
#include <map>
#include <queue>
#include <iostream>
#include <sstream>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstring>
#include <cctype>
#include <cassert>
#define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i))
#define rer(i,l,u) for(int (i)=(int)(l);(i)<=(int)(u);++(i))
#define reu(i,l,u) for(int (i)=(int)(l);(i)<(int)(u);++(i))
#if 1 || defined(_MSC_VER) || __cplusplus > 199711L
#define aut(r,v) auto r = (v)
#else
#define aut(r,v) typeof(v) r = (v)
#endif
#define each(it,o) for(aut(it, (o).begin()); it != (o).end(); ++ it)
#define all(o) (o).begin(), (o).end()
#define pb(x) push_back(x)
#define mp(x,y) make_pair((x),(y))
#define mset(m,v) memset(m,v,sizeof(m))
#define INF 0x3f3f3f3f
#define INFL 0x3f3f3f3f3f3f3f3fLL
using namespace std;
typedef vector<int> vi; typedef pair<int,int> pii; typedef vector<pair<int,int> > vpii;
typedef long long ll; typedef vector<long long> vl; typedef pair<long long,long long> pll; typedef vector<pair<long long,long long> > vpll;
typedef vector<string> vs; typedef long double ld;
template<typename T, typename U> inline void amin(T &x, U y) { if(y < x) x = y; }
template<typename T, typename U> inline void amax(T &x, U y) { if(x < y) x = y; }

struct CentroidPathDecomposition {
	vector<int> colors, positions;	//Vertex -> Color, Vertex -> Offset
	vector<int> lengths, parents, branches;	//Color -> Int, Color -> Color, Color -> Offset
	vector<int> parentnodes, depths;	//Vertex -> Vertex, Vertex -> Int
	//vector<FenwickTree>とかを避けて1次元にしたい時に使う
	vector<int> sortednodes, offsets;	//Index -> Vertex, Color -> Index

	//両方の辺があってもいいし、親から子への辺だけでもよい
	void build(const vector<vi> &g, int root) {
		int n = g.size();

		colors.assign(n, -1); positions.assign(n, -1);
		lengths.clear(); parents.clear(); branches.clear();
		parentnodes.assign(n, -1); depths.assign(n, -1);

		vector<int> subtreesizes;
		measure(g, root, subtreesizes);

		struct State {
			int i, len, parent;
			State() { }
			State(int i_, int l, int p): i(i_), len(l), parent(p) { }
		};
		depths[root] = 0;
		vector<State> s;
		s.push_back(State(root, 0, -1));
		while(!s.empty()) {
			State t = s.back(); s.pop_back();
			int i = t.i, len = t.len;
			int color = lengths.size();
			if(t.parent != -2) {
				assert(parents.size() == color);
				parents.push_back(t.parent);
				branches.push_back(len);
				len = 0;
			}
			colors[i] = color;
			positions[i] = len;

			int maxsize = -1, maxj = -1;
			each(j, g[i]) if(colors[*j] == -1) {
				if(maxsize < subtreesizes[*j]) {
					maxsize = subtreesizes[*j];
					maxj = *j;
				}
				parentnodes[*j] = i;
				depths[*j] = depths[i] + 1;
			}
			if(maxj == -1) {
				lengths.push_back(len + 1);
			}else {
				each(j, g[i]) if(colors[*j] == -1 && *j != maxj)
					s.push_back(State(*j, len, color));
				s.push_back(State(maxj, len + 1, -2));
			}
		}

		sortNodes();
	}

	void sortNodes() {
		int n = colors.size(), m = lengths.size();
		sortednodes.resize(n, -1);
		offsets.resize(m + 1);
		rep(i, m) offsets[i+1] = offsets[i] + lengths[i];
		rep(i, n) sortednodes[offsets[colors[i]] + positions[i]] = i;
	}

	void get(int v, int &c, int &p) const {
		c = colors[v]; p = positions[v];
	}
	bool go_up(int &c, int &p) const {
		p = branches[c]; c = parents[c];
		return c != -1;
	}

	inline const int *nodesBegin(int c) const { return &sortednodes[0] + offsets[c]; }
	inline const int *nodesEnd(int c) const { return &sortednodes[0] + offsets[c+1]; }

private:
	void measure(const vector<vi> &g, int root, vector<int> &out_subtreesizes) const {
		out_subtreesizes.assign(g.size(), -1);
		vector<int> s;
		s.push_back(root);
		while(!s.empty()) {
			int i = s.back(); s.pop_back();
			if(out_subtreesizes[i] == -2) {
				int s = 1;
				each(j, g[i]) if(out_subtreesizes[*j] != -2)
					s += out_subtreesizes[*j];
				out_subtreesizes[i] = s;
			}else {
				s.push_back(i);
				each(j, g[i]) if(out_subtreesizes[*j] == -1)
					s.push_back(*j);
				out_subtreesizes[i] = -2;
			}
		}
	}
};


typedef int Vertex;
struct Graph {
	typedef std::pair<Vertex, Vertex> Edge;
	struct To {
		Vertex to;
	};

	int n, m;

	Graph(int n_, const std::vector<Edge> &edges):
		n(n_), m(edges.size()), tos(m+1), offsets(n+1, 0) {
		for(int e = 0; e < m; e ++) offsets[edges[e].first] ++;
		for(int v = 1; v <= n; v ++) offsets[v] += offsets[v-1];
		for(int e = 0; e < m; e ++)
			tos[-- offsets[edges[e].first]].to = edges[e].second;
	}

	inline const To *edgesBegin(int v) const { return &tos[offsets[v]]; }
	inline const To *edgesEnd(int v) const { return &tos[offsets[v+1]]; }

	inline const int outDegree(int v) const { return offsets[v+1] - offsets[v]; }

private:
	std::vector<To> tos;
	std::vector<int> offsets;
};
	
class SchieberVishkinLCA {
public:
	typedef unsigned Word;
private:

	static inline Word lowestOneBit(Word v) {
		return v & (~v+1);
	}
	static inline Word highestOneBitMask(Word v) {
		v |= v >> 1;
		v |= v >> 2;
		v |= v >> 4;
		v |= v >> 8;
		v |= v >> 16;
		return v >> 1;
	}

	std::vector<Word> indices;			//Vertex -> index
	std::vector<Word> maxHIndices;		//Vertex -> index
	std::vector<Word> ancestorHeights;	//Vertex -> Word
	std::vector<Vertex> pathParents;	//index-1 -> Vertex
public:
	//gは親→子の枝のある根付き木
	void build(const Graph &g, Vertex root) {
		assert(g.m == g.n - 1);

		ancestorHeights.resize(g.n);
		std::vector<Vertex> parents(g.n);
		maxHIndices.resize(g.n);
		std::vector<Vertex> vertices; vertices.reserve(g.n);
		indices.resize(g.n);

		//euler tour
		Word currentIndex = 1;
		parents[root] = root;	//利便さのために
		vertices.push_back(root);
		while(!vertices.empty()) {
			Vertex v = vertices.back(); vertices.pop_back();
			indices[v] = currentIndex ++;
			for(const Graph::To *it = g.edgesBegin(v); it != g.edgesEnd(v); ++ it) {
				parents[it->to] = v;
				vertices.push_back(it->to);
			}
		}

		//BFS (トポロジカル順序を求めるために)
		int head = 0, tail = 1;
		vertices.resize(g.n); vertices[0] = root;
		while(head != tail) {
			Vertex v = vertices[head ++];
			for(const Graph::To *it = g.edgesBegin(v); it != g.edgesEnd(v); ++ it)
				vertices[tail ++] = it->to;
		}

		//深い方から
		for(std::vector<Vertex>::const_iterator it = vertices.begin(); it != vertices.end(); ++ it)
			maxHIndices[*it] = indices[*it];
		for(std::vector<Vertex>::const_reverse_iterator it = vertices.rbegin(); it != vertices.rend(); ++ it) {
			Vertex v = *it, parent = parents[v];
			if(lowestOneBit(maxHIndices[parent]) < lowestOneBit(maxHIndices[v]))
				maxHIndices[parent] = maxHIndices[v];
		}

		//Aを求める
		ancestorHeights[root] = 0;
		for(std::vector<Vertex>::const_iterator it = vertices.begin(); it != vertices.end(); ++ it) {
			Vertex v = *it;
			ancestorHeights[v] = ancestorHeights[parents[v]] | lowestOneBit(maxHIndices[v]);
		}

		pathParents.swap(parents);	//メモリをけちる
		pathParents[indices[root]-1] = root;
		for(std::vector<Vertex>::const_iterator it = vertices.begin(); it != vertices.end(); ++ it) {
			Vertex v = *it;
			for(const Graph::To *jt = g.edgesBegin(v); jt != g.edgesEnd(v); ++ jt) {
				if(maxHIndices[v] != maxHIndices[jt->to])
					pathParents[indices[jt->to]-1] = v;
				else
					pathParents[indices[jt->to]-1] = pathParents[indices[v]-1];
			}
		}
	}

	Vertex query(Vertex v, Vertex u) const {
		//binary tree上でのLCAの高さを求める
		Word Iv = maxHIndices[v], Iu = maxHIndices[u];
		Word hIv = lowestOneBit(Iv), hIu = lowestOneBit(Iu);
		Word hbMask = highestOneBitMask((Iv ^ Iu) | hIv | hIu);
		Word j = lowestOneBit(ancestorHeights[v] & ancestorHeights[u] & ~hbMask);
		//j = hI(lca(v,u)) となる (ここで、hI(x) = 2^(complete binary tree上でのI(x)の高さ), I(x) = maxHIndices[x])
		//(hI(lca(v,u)) = j)はhI(v)かhI(u)かその他の最大値。そして一意であることを考えると…
		Vertex x, y;
		if(j == hIv) x = v;
		else {			//lcaはvのパス上には無い
			Word kMask = highestOneBitMask(ancestorHeights[v] & (j-1));	//vの祖先で、jよりは低いけどその中で一番上にあるパス
			x = pathParents[(indices[v] & ~kMask | (kMask+1))-1];	//indices[v]のkの高さの祖先のパスの親
		}
		if(j == hIu) y = u;
		else {			//lcaはuのパス上には無い
			Word kMask = highestOneBitMask(ancestorHeights[u] & (j-1));	//uの祖先で、jよりは低いけどその中で一番上にあるパス
			y = pathParents[(indices[u] & ~kMask | (kMask+1))-1];	//indices[u]のkの高さの祖先のパスの親
		}
		return indices[x] < indices[y] ? x : y;	//in-orderなので、インデックスが低い方が祖先なはず
	}
};

struct FenwickTree {
	typedef long long T;
	vector<T> v;
	FenwickTree() { }
	void init(int n) { v.assign(n, 0); }
	void add(int i, T x) {
		for(; i < (int)v.size(); i |= i+1) v[i] += x;
	}
	T sum(int i) {	//[0, i)
		T r = 0;
		for(-- i; i >= 0; i = (i & (i+1)) - 1) r += v[i];
		return r;
	}
	T sum(int left, int right) {	//[left, right)
		return sum(right) - sum(left);
	}
};

void direct_tree(const vector<vi> &g, int i, int parent, vector<pii> &out_edges) {
	each(j, g[i]) if(*j != parent) {
		out_edges.pb(mp(i, *j));
		direct_tree(g, *j, i, out_edges);
	}
}

int cost[2000];
int main() {
	int N;
	scanf("%d", &N);
	rep(i, N) scanf("%d", &cost[i]);
	vector<vi> g(N);
	rep(i, N-1) {
		int a, b;
		scanf("%d%d", &a, &b); a --, b --;
		g[a].pb(b); g[b].pb(a);
	}
	vector<Graph::Edge> edges;
	direct_tree(g, 0, -1, edges);
	SchieberVishkinLCA lca; lca.build(Graph(N, edges), 0);
	CentroidPathDecomposition cpd; cpd.build(g, 0);
	vector<FenwickTree> ft(cpd.lengths.size());
	rep(i, ft.size())
		ft[i].init(cpd.lengths[i]);
	rep(i, N) {
		int c, p;
		cpd.get(i, c, p);
		ft[c].add(p, cost[i]);
	}
	int Q;
	scanf("%d", &Q);
	rep(ii, Q) {
		int t;
		scanf("%d", &t);
		if(t == 0) {
			int A, B;
			scanf("%d%d", &A, &B); A --, B --;
			int C = lca.query(A, B);
			int c, p, lc, lp;
			cpd.get(C, lc, lp);
			cpd.get(A, c, p);
			ll ans = 0;
			while(1) {
				int up = c != lc ? 0 : lp;
				ans += ft[c].sum(up, p+1);
				if(c == lc) break;
				cpd.go_up(c, p);
			}
			cpd.get(B, c, p);
			while(1) {
				int up = c != lc ? 0 : lp+1;
				ans += ft[c].sum(up, p+1);
				if(c == lc) break;
				cpd.go_up(c, p);
			}
			printf("%lld\n", ans);
		}else {
			int A, C;
			scanf("%d%d", &A, &C); A --;
			int c, p;
			cpd.get(A, c, p);
			ft[c].add(p, C);
		}
	}
	return 0;
}
accuracy: 1
time_ms: 380
memory_kb: 1588
final_rank: 1