Problem:
Weighted Union Find Trees
There is a sequence $A = a_0, a_1, ..., a_{n-1}$. You are given the following information and questions.
relate$(x, y, z)$: $a_y$ is greater than $a_x$ by $z$
diff$(x, y)$: report the difference between $a_x$ and $a_y$ $(a_y - a_x)$
Input
$n \; q$
$query_1$
$query_2$
:
$query_q$
In the first line, $n$ and $q$ are given. Then, $q$ information/questions are given in the following format.
0 $x \; y\; z$
or
1 $x \; y$
where '
0
' of the first digit denotes the relate information and '
1
' denotes the diff question.
Output
For each diff question, print the difference between $a_x$ and $a_y$ $(a_y - a_x)$.
Constraints
$2 \leq n \leq 100,000$
$1 \leq q \leq 200,000$
$0 \leq x, y < n$
$x \ne y$
$0 \leq z \leq 10000$
There are no inconsistency in the given information
Sample Input
5 6
0 0 2 5
0 1 2 3
1 0 1
1 1 3
0 1 4 8
1 0 4
Sample Output
2
?
10

Submitted solutions:

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

struct Weighted_UnionFind {
    vector<int> par, rank, siz;
    vector<ll> weight;

    Weighted_UnionFind(int N) : par(N,-1), rank(N, 0), siz(N,1), weight(N,0) {}
    
    int root(int x) {
        if (par[x] == -1) return x;
        int r = root(par[x]);
        weight[x] += weight[par[x]];
        return par[x] = r;
    }

    ll weight_of(int x) {
        root(x);
        return weight[x];
    }

    void relate(int x, int y, ll w) {
        ll wx = weight_of(x), wy = weight_of(y);
        int rx = root(x), ry = root(y);
        if (rx == ry) return;

        ll d = w + wx - wy;
        if (rank[rx] < rank[ry]) { swap(rx, ry); d = -d; }

        par[ry] = rx;
        weight[ry] = d;
        siz[rx] += siz[ry];
        if (rank[rx] == rank[ry]) rank[rx]++;
    }

    ll diff(int x, int y) {
        if (root(x) != root(y)) return LLONG_MAX;
        return weight_of(y) - weight_of(x);
    }
};

int main() {
    int N, Q;
    cin >> N >> Q;

    Weighted_UnionFind w_uf(N);
    for (int qi = 0; qi < Q; qi++) {
        int type; cin >> type;
        if (type == 0) {
            int x, y; ll w;
            cin >> x >> y >> w;
            w_uf.relate(x, y, w); //a[y] > a[x]　差はz
        } else if (type == 1) {
            int x, y;
            cin >> x >> y;
            ll d = w_uf.diff(x, y); //a[y]とa[x]の差を求める
            if (d == LLONG_MAX) cout << "?" << endl;
            else cout << d << endl;
        }
    }
    return 0;
}
accuracy: 1
time_ms: 160
memory_kb: 4936
final_rank: 18

code_snippet: #include<bits/stdc++.h>
using namespace std;
template<class T>
struct weighted_uf{
	vector<int>a;
	vector<T>w;
	weighted_uf(int n):a(n,-1),w(n){}
	int root(int u){
		if(a[u]<0)return u;
		int v=a[u];
		a[u]=root(a[u]);
		w[u]+=w[v];
		return a[u];
	}
	bool same(int u,int v){return root(u)==root(v);}
	T diff(int u,int v){
		assert(same(u,v));
		return w[v]-w[u];
	}
	bool unite(int u,int v,T d){
		root(u),root(v);
		d+=w[u]-w[v];
		u=root(u),v=root(v);
		if(u==v){assert(d==0);return 0;}
		if(a[u]>a[v])swap(u,v),d=-d;
		a[u]+=a[v];
		w[v]=d;
		a[v]=u;
		return 1;
	}
};
int main(){
	cin.tie(0)->sync_with_stdio(0);
	int n,q;
	cin>>n>>q;
	weighted_uf<int>uf(n);
	while(q--){
		int op,u,v;
		cin>>op>>u>>v;
		if(op==0){
			int w;
			cin>>w;
			uf.unite(u,v,w);
		}
		if(op==1){
			if(uf.root(u)==uf.root(v)){
				cout<<uf.diff(u,v)<<'\n';
			}else{
				cout<<'?'<<'\n';
			}
		}
	}
}
accuracy: 1
time_ms: 30
memory_kb: 3744
final_rank: 2

code_snippet: #include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<(int)(n);++i)
#define revrep(i,n) for(int i=(int)(n)-1;i>=0;--i)
using namespace std;
using ll = long long;

class WUF {

private:
    vector<int> parent;
    vector<int> diff_weight;

public:
    WUF(int n):parent(n),diff_weight(n){
        iota(parent.begin(),parent.end(),0);
    }
    
    int find(int x){
        if(parent[x]==x) return x;
        
        int r = find(parent[x]);
        diff_weight[x] += diff_weight[parent[x]];
        parent[x] = r;
        return r;
    }
    
    bool same(int x,int y){
        return find(x) == find(y);
    }
    
    bool unite(int x,int y,int z){
        int root_x = find(x);
        int root_y = find(y);
        
        if(root_x!=root_y){
            parent[root_y] = root_x;
            diff_weight[root_y] = z + diff_weight[x] - diff_weight[y];
            return true;
        }else return false;
    }
    
    string diff(int x,int y){
        if(!same(x,y)) return "?";
        int ans = diff_weight[y] - diff_weight[x];
        return to_string(ans);
    }
    
};


int main(void){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n,Q;cin>>n>>Q;
    WUF S(n);
    
    while(Q--){
        int com;cin>>com;
        if(com==0){
            int x,y,z;cin>>x>>y>>z;
            S.unite(x,y,z);
        }else{
            int x,y;cin>>x>>y;
            cout << S.diff(x,y) << endl;
        }
    }
    return 0;
}
accuracy: 1
time_ms: 100
memory_kb: 3880
final_rank: 6

code_snippet: #define _USE_MATH_DEFINES
#include<bits/stdc++.h>
#define OVERLOAD_REP(v1, v2, v3, v4, NAME, ...) NAME
#define REP1(i, n) for (int i = 0; (i) < (n); ++(i))
#define REP2(i, l, r) for (int i = (l); (i) < (r); ++(i))
#define REP3(i, l, r, d) for (int i = (l); (i) < (r); (i)+=(d))
#define rep(...) OVERLOAD_REP(__VA_ARGS__, REP3, REP2, REP1)(__VA_ARGS__)
#define OVERLOAD_PRE(v1, v2, v3, v4, NAME, ...) NAME
#define PRE1(i, n) for (int i = (n)-1; (i) >= 0; --(i)) // [0,n)
#define PRE2(i, l, r) for (int i = (r)-1; (i) >= (l); --(i)) //[l,r)
#define PRE3(i, l, r, d) for (int i = (r)-1; (i) >= (l); (i)-=(d))
#define pre(...) OVERLOAD_PRE(__VA_ARGS__, PRE3, PRE2, PRE1)(__VA_ARGS__)
#define bg begin()
#define en end()
#define rbg rbegin()
#define ren rend()
#define all(x) x.bg,x.en
#define rall(x) x.rbg,x.ren
#define pf push_front
#define pb push_back
#define eb emplace_back
#define fir first
#define sec second
#define sz(x) ((int)(x).size())
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using pii = pair<int,int>;
using pll = pair<ll,ll>;
using sti = stack<int>;
using sei = set<int>;
using qi = queue<int>;
using qii = queue<pii>;
using dqi = deque<int>;
template<class t, class s> using umap = unordered_map<t,s>;
template<class t> using uset = unordered_set<t>;
template<class t> using mset = multiset<t>;
template<class t> using pq=priority_queue<t>;
template<class t> using pqg=priority_queue<t,vector<t>, greater<t>>;
template<class t> using vc=vector<t>;
template<class t> using vvc=vc<vc<t>>;
template<class t> using vvvc=vc<vc<vc<t>>>;
using vi=vc<int>;
using vvi=vc<vc<int>>;
using vll=vc<ll>;
using vvll=vc<vc<ll>>;
using vd=vc<double>;
using vvd=vc<vc<double>>;
using vb=vc<bool>;
using vvb=vc<vc<bool>>;
using vch=vc<char>;
using vs=vc<string>;
const int inf = 1001001001;
const ll infl = 1LL << 60;
const ll mod = 998244353;
template<class t,class u> bool chmax(t&a,u b){if(a<b){a=b; return true;} return false;}
template<class t,class u> bool chmin(t&a,u b){if(a>b){a=b; return true;} return false;}
void yes(){ cout << "Yes" << '\n'; }
void no(){ cout << "No" << '\n'; }

/*  WeightedUnionFind 
	https://www.youtube.com/live/ugeZCP6JxqQ?&t=8302

	root(x): xの根。O(α(n))
	size(x): xを含む集合の要素数。
	same(x,y): xとyが同じ集合にいるか。O(α(n))
	merge(x,y): xとyを同じ集合にする。y->xに重さdの辺を張る。 O(α(n))
	groups(): 「一つの連結成分の頂点番号のリスト」のリスト
	diff(x,y): y->xを返す。
*/
template<class T>
struct WeightedUF{
	int _n;
	vi par;
	vc<T> dif;
	WeightedUF(): _n(0) {}
	WeightedUF(int n): _n(n),par(n,-1), dif(n) {}
	int root(int x){
		if(par[x] < 0) return x;
		int nroot=root(par[x]);
		dif[x] += dif[par[x]];
		par[x] = nroot; //経路圧縮
		return nroot;
	}
	int size(int x){
		return -par[root(x)];
	}
	bool same(int x, int y){
		return root(x) == root(y);
	}
	bool merge(int x, int y, T d){ //merge成功のときtrue
		int rx=root(x), ry=root(y);
		d += dif[x]-dif[y];
		if(rx == ry) return d == 0;
		if(-par[x] < -par[y]){
			swap(rx,ry);
			d*=-1;
		}
		par[rx]+=par[ry];
		par[ry]=rx;
		dif[ry]=d;
		return true;
	}
	vvi groups() {
        vi roots(_n), si(_n);
        rep(i,_n){
            roots[i] = root(i);
            si[roots[i]]++;
        }
        vvi result(_n);
        rep(i,_n) result[i].reserve(si[i]);
        rep(i,_n) result[roots[i]].pb(i);
        result.erase(
            remove_if(result.begin(), result.end(), [&](const vi& v) { return v.empty(); }),
            result.end()
        );
        return result;
    }
	T diff(int x, int y) {
		if(!same(x,y)) return numeric_limits<T>::max();
        return dif[y] - dif[x];
    }
};

int main(){	
	ios::sync_with_stdio(false);
  	cin.tie(0);
	int n,q; cin >> n >> q;
    WeightedUF<ll> uf(n);
    rep(i,q){
        int type; cin >> type;
        if(type==0){
            int x,y; ll z; cin >> x >> y >> z;
            uf.merge(x,y,z);
        } else {
            int x,y; cin >> x >> y;
            if(uf.diff(x,y)==numeric_limits<ll>::max())cout << '?' << endl;
            else cout << uf.diff(x,y) << endl;
        }
    }
	return 0;
}
accuracy: 1
time_ms: 100
memory_kb: 4772
final_rank: 7

code_snippet: #include <stdio.h>
#include <cmath>
#include <algorithm>
#include <cfloat>
#include <stack>
#include <queue>
#include <vector>
#include <string>
#include <iostream>
#include <set>
#include <map>
#include <time.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define NUM 100005

int height[NUM]; //同じグループに属しているかどうかだけを調べるために使用
int calc_parent[NUM];
int relative_weight[NUM]; //自分のボスとの相対的な重さの差を持つ配列

struct Info{
	void set(int arg_parent_id,int arg_weight_sum){
		parent_id = arg_parent_id;
		weight_sum = arg_weight_sum;
	}
	int parent_id,weight_sum;
};

Info find_parent_and_calc_weight(int id){
	if(calc_parent[id] == id){
		Info ret;
		ret.set(id,0); //大ボスの相対的重さは必ず0
		return ret;
	}
	else{
		Info ret = find_parent_and_calc_weight(calc_parent[id]); //大ボスのidと、自分より上の相対的重さの総和を取得
		calc_parent[id] = ret.parent_id;
		relative_weight[id] += ret.weight_sum; //自分より上の相対的重さを足す
		ret.weight_sum = relative_weight[id];

		return ret;
	}
}


//グループの統合、および相対的重さ木の統合を行う
void unite(int a,int b,int w){

	//まずは
	Info a_info = find_parent_and_calc_weight(a);
	Info b_info = find_parent_and_calc_weight(b);

	if(a_info.parent_id == b_info.parent_id)return; //大ボスが同じならreturn

	//統合処理
	if(height[a_info.parent_id] == height[b_info.parent_id]){ //木の高さが同じ場合はaに併合する

		relative_weight[b_info.parent_id] = w-(relative_weight[b]-relative_weight[a]);
		calc_parent[b_info.parent_id] = a_info.parent_id;

	}else if(height[a_info.parent_id] == height[b_info.parent_id]){ //木の高さが高い方に併合する
		relative_weight[b_info.parent_id] = w-(relative_weight[b]-relative_weight[a]);
		calc_parent[b_info.parent_id] = a_info.parent_id;
		height[a_info.parent_id]++;
	}else{

		relative_weight[a_info.parent_id] = (relative_weight[b]-relative_weight[a])-w;
		calc_parent[a_info.parent_id] = b_info.parent_id;
	}
}

bool isSame(int a,int b){
	Info left = find_parent_and_calc_weight(a);
	Info right = find_parent_and_calc_weight(b);

	return left.parent_id == right.parent_id;
}

int main(){

	int N,Q;
	scanf("%d %d",&N,&Q);

	for(int i = 0; i <= N; i++){
		height[i] = 0;
		calc_parent[i] = i;
		relative_weight[i] = 0; //初期値は0にしておく
	}

	int command,A,B,value;
	for(int loop = 0; loop < Q; loop++){
		scanf("%d",&command);

		if(command == 0){
			scanf("%d %d %d",&A,&B,&value);
			unite(A,B,value);
		}else{
			scanf("%d %d",&A,&B);
			if(!isSame(A,B)){ //★ここでそれぞれの経路圧縮&weight計算が行われる★
				printf("?\n");
			}else{
				printf("%d\n",relative_weight[B]-relative_weight[A]);
			}
		}
	}

	return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 4736
final_rank: 4

code_snippet: #include <iostream>
#include <vector>
using namespace std;
//レビューの力を借りたプログラムです

class DisjointSet {
    vector<int> parent, rank, weight;
    int INT_MAX = 10001;

public:
    DisjointSet(int n) {
        parent.resize(n);
        rank.resize(n, 0);
        weight.resize(n, 0);
        for (int i = 0; i < n; i++) {
            parent[i] = i;
        }
    }

    int findSet(int x) {
        if (parent[x] != x) {
            int orig = parent[x];
            parent[x] = findSet(parent[x]);
            weight[x] += weight[orig];
        }
        return parent[x];
    }

    void unionSet(int x, int y, int w) {
        int rootX = findSet(x);
        int rootY = findSet(y);
        if (rootX != rootY) {
            if (rank[rootX] < rank[rootY]) {
                parent[rootX] = rootY;
                weight[rootX] = weight[y] - weight[x] - w;
            } else if (rank[rootX] > rank[rootY]) {
                parent[rootY] = rootX;
                weight[rootY] = weight[x] - weight[y] + w;
            } else {
                parent[rootY] = rootX;
                weight[rootY] = weight[x] - weight[y] + w;
                rank[rootX]++;
            }
        }
    }

    int diff(int x, int y) {
        if (findSet(x) == findSet(y)) {
            return weight[y] - weight[x];
        }
        return INT_MAX;
    }
};

int main() {
    int n, q;
    int INT_MAX = 10001;
    cin >> n >> q;
    DisjointSet ds(n);

    for (int i = 0; i < q; i++) {
        int queryType, x, y, z;
        cin >> queryType;
        if (queryType == 0) {
            cin >> x >> y >> z;
            ds.unionSet(x, y, z);
        } else if (queryType == 1) {
            cin >> x >> y;
            int result = ds.diff(x, y);
            if (result == INT_MAX) {
                cout << "?" << endl;
            } else {
                cout << result << endl;
            }
        }
    }
    return 0;
}
accuracy: 1
time_ms: 160
memory_kb: 4112
final_rank: 9

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

#define N 100010

int a[N],w[N];

int Find(int x){
	if(x==a[x])
		return x;
	int t=Find(a[x]);
	w[x]+=w[a[x]];
	return a[x]=t;
}

void Union(int x,int y,int z){
	int u=Find(x),v=Find(y);
    if(u==v)
		assert(w[y]-w[x]==z);
	else{
		a[v]=u;
		w[v]=w[x]-w[y]+z;
	}
}

int main(){
	int n,q;
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)
		a[i]=i;
	for(int op,x,y,z;q--;){
		scanf("%d%d%d",&op,&x,&y);
		x++,y++;
		if(!op){
			scanf("%d",&z);
			Union(x,y,z);
		}
		else{
			if(Find(x)==Find(y))
				printf("%d\n",w[y]-w[x]);
			else
				puts("?");
		}
	}
	return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 4344
final_rank: 3

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i=0;i<n;i++)
using ll=long long;

struct wdsu{
    vector<int> p;
    vector<ll> wd;
    wdsu(int _n):p(_n,-1),wd(_n,0){}
    int root(int x){
        if(p[x]<0) return x;
        int r=root(p[x]);
        wd[x]+=wd[p[x]];
        return p[x]=r;}
    bool same(int x,int y){return root(x)==root(y);}
    int size(int x){return -p[root(x)];}
    void add(int _n=1){rep(i,_n){p.push_back(-1);wd.push_back(0);}}
    ll weight(int x){root(x);return wd[x];}
    void relate(int x,int y,ll w){ //weight(y)はweight(x)よりw大きい
        w=w+weight(x)-weight(y);
        x=root(x),y=root(y);
        if(x==y) return;
        if(p[x]>p[y]){swap(x,y);w=-w;}
        p[x]+=p[y]; p[y]=x; wd[y]=w;}
    ll diff(int x,int y){return weight(y)-weight(x);}
};

int main(){
    int n,q;cin>>n>>q;
    wdsu d(n);
    while(q--){
        int cmd;cin>>cmd;
        if(cmd==0){
            int x,y;ll z;cin>>x>>y>>z;
            d.relate(x,y,z);
        }
        if(cmd==1){
            int x,y;cin>>x>>y;
            if(!d.same(x,y)) cout<<"?\n";
            else cout<<d.diff(x,y)<<"\n";
        }
    }
    return 0;
}
accuracy: 1
time_ms: 160
memory_kb: 4320
final_rank: 13

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i=0;i<n;i++)
using ll=long long;

struct wdsu{
    vector<int> p;
    vector<ll> wd;
    wdsu(int _n=0):p(_n,-1),wd(_n,0){}
    int root(int x){
        if(p[x]<0) return x;
        int r=root(p[x]);
        wd[x]+=wd[p[x]];
        return p[x]=r;}
    bool same(int x,int y){return root(x)==root(y);}
    int size(int x){return -p[root(x)];}
    void add(int _n=1){rep(i,_n){p.push_back(-1);wd.push_back(0);}}
    ll weight(int x){root(x);return wd[x];}
    void relate(int x,int y,ll w){ //weight(y)はweight(x)よりw大きい
        w=w+weight(x)-weight(y);
        x=root(x),y=root(y);
        if(x==y) return;
        if(p[x]>p[y]){swap(x,y);w=-w;}
        p[x]+=p[y]; p[y]=x; wd[y]=w;}
    ll diff(int x,int y){return weight(y)-weight(x);}
};

int main(){
    int n,q;cin>>n>>q;
    wdsu d;
    d.add(n);
    while(q--){
        int cmd;cin>>cmd;
        if(cmd==0){
            int x,y;ll z;cin>>x>>y>>z;
            d.relate(x,y,z);
        }
        if(cmd==1){
            int x,y;cin>>x>>y;
            if(!d.same(x,y)) cout<<"?\n";
            else cout<<d.diff(x,y)<<"\n";
        }
    }
    return 0;
}
accuracy: 1
time_ms: 160
memory_kb: 4464
final_rank: 15

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i=0;i<n;i++)
using ll=long long;

struct wdsu{
    vector<int> p;
    vector<ll> wd;
    wdsu(int _n=0):p(_n,-1),wd(_n,0){}
    int root(int x){
        if(p[x]<0) return x;
        int r=root(p[x]);
        wd[x]+=wd[p[x]];
        return p[x]=r;}
    bool same(int x,int y){return root(x)==root(y);}
    int size(int x){return -p[root(x)];}
    void add(int _n=1){rep(i,_n){p.push_back(-1);wd.push_back(0);}}
    ll weight(int x){root(x);return wd[x];}
    void relate(int x,int y,ll w){ //weight(y)はweight(x)よりw大きい
        w=w+weight(x)-weight(y);
        x=root(x),y=root(y);
        if(x==y) return;
        if(p[x]>p[y]){swap(x,y);w=-w;}
        p[x]+=p[y]; p[y]=x; wd[y]=w;}
    ll diff(int x,int y){return weight(y)-weight(x);}
};

int main(){
    int n,q;cin>>n>>q;
    wdsu d(n);
    while(q--){
        int cmd;cin>>cmd;
        if(cmd==0){
            int x,y;ll z;cin>>x>>y>>z;
            d.relate(x,y,z);
        }
        if(cmd==1){
            int x,y;cin>>x>>y;
            if(!d.same(x,y)) cout<<"?\n";
            else cout<<d.diff(x,y)<<"\n";
        }
    }
    return 0;
}
accuracy: 1
time_ms: 170
memory_kb: 4100
final_rank: 16

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

struct wdsu{
    vector<int> p;
    vector<ll> wd;
    wdsu(int _n=0):p(_n,-1),wd(_n){}
    int root(int x){
        if(p[x]<0) return x;
        int r=root(p[x]);
        wd[x]+=wd[p[x]];
        return p[x]=r;}
    bool same(int x,int y){return root(x)==root(y);}
    int size(int x){return -p[root(x)];}
    ll weight(int x){root(x);return wd[x];}
    void relate(int x,int y,ll w){ //weight(y)はweight(x)よりw大きい
        w=w+weight(x)-weight(y);
        x=root(x),y=root(y);
        if(x==y) return;
        if(p[x]>p[y]){swap(x,y);w=-w;}
        p[x]+=p[y]; p[y]=x; wd[y]=w;}
    ll diff(int x,int y){return weight(y)-weight(x);}
};

int main(){
    int n,q;cin>>n>>q;
    wdsu d(n);
    while(q--){
        int cmd;cin>>cmd;
        if(cmd==0){
            int x,y;ll z;cin>>x>>y>>z;
            d.relate(x,y,z);
        }
        if(cmd==1){
            int x,y;cin>>x>>y;
            if(!d.same(x,y)) cout<<"?\n";
            else cout<<d.diff(x,y)<<"\n";
        }
    }
    return 0;
}
accuracy: 1
time_ms: 160
memory_kb: 4140
final_rank: 10

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

struct wdsu{
    vector<int> p;
    vector<ll> wd;
    wdsu(int _n=0):p(_n,-1),wd(_n){}
    int root(int x){
        if(p[x]<0) return x;
        int r=root(p[x]);
        wd[x]+=wd[p[x]];
        return p[x]=r;}
    bool same(int x,int y){return root(x)==root(y);}
    ll weight(int x){root(x);return wd[x];}
    void relate(int x,int y,ll w){ //weight(y)はweight(x)よりw大きい
        w=w+weight(x)-weight(y);
        x=root(x),y=root(y);
        if(x==y) return;
        if(p[x]>p[y]){swap(x,y);w=-w;}
        p[x]+=p[y]; p[y]=x; wd[y]=w;}
    ll diff(int x,int y){return weight(y)-weight(x);}
};

int main(){
    int n,q;cin>>n>>q;
    wdsu d(n);
    while(q--){
        int cmd;cin>>cmd;
        if(cmd==0){
            int x,y;ll z;cin>>x>>y>>z;
            d.relate(x,y,z);
        }
        if(cmd==1){
            int x,y;cin>>x>>y;
            if(!d.same(x,y)) cout<<"?\n";
            else cout<<d.diff(x,y)<<"\n";
        }
    }
    return 0;
}
accuracy: 1
time_ms: 160
memory_kb: 4320
final_rank: 13

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

struct wdsu{
    vector<int> p;
    vector<int> wd;
    wdsu(int _n=0):p(_n,-1),wd(_n){}
    int root(int x){
        if(p[x]<0) return x;
        int r=root(p[x]);
        wd[x]+=wd[p[x]];
        return p[x]=r;}
    bool same(int x,int y){return root(x)==root(y);}
    ll weight(int x){root(x);return wd[x];}
    void relate(int x,int y,int w){ //weight(y)はweight(x)よりw大きい
        w=w+weight(x)-weight(y);
        x=root(x),y=root(y);
        if(x==y) return;
        if(p[x]>p[y]){swap(x,y);w=-w;}
        p[x]+=p[y]; p[y]=x; wd[y]=w;}
    ll diff(int x,int y){return weight(y)-weight(x);}
};

int main(){
    int n,q;cin>>n>>q;
    wdsu d(n);
    while(q--){
        int cmd;cin>>cmd;
        if(cmd==0){
            int x,y,z;cin>>x>>y>>z;
            d.relate(x,y,z);
        }
        if(cmd==1){
            int x,y;cin>>x>>y;
            if(!d.same(x,y)) cout<<"?\n";
            else cout<<d.diff(x,y)<<"\n";
        }
    }
    return 0;
}
accuracy: 1
time_ms: 160
memory_kb: 3904
final_rank: 8

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

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

template <class T>
struct PotentializedUnionFind {
    int n, counter;
    vector<int> data;
    vector<T> potential;
    PotentializedUnionFind(int size) : n(size), counter(size), data(size, -1), potential(size, 0) {}
    int root(int x) {
        if (data[x] < 0) return x;
        int r = root(data[x]);
        potential[x] += potential[data[x]];
        return data[x] = r;
    }
    bool same(int x, int y) { return root(x) == root(y); }
    // U[y] - U[x] = cost
    bool merge(int x, int y, T cost) {
        int rx = root(x), ry = root(y);
        if (rx == ry) return false;
        if (-data[rx] < -data[ry]) swap(x, y), swap(rx, ry), cost = -cost;
        data[rx] += data[ry];
        data[ry] = rx;
        potential[ry] = potential[x] - potential[y] + cost;
        counter--;
        return true;
    }
    T diff(int x, int y) { return potential[y] - potential[x]; }
    int size(int x) { return -data[root(x)]; }
    int size() { return counter; }
    vector<pair<int, T>> group(int x) {
        vector<pair<int, T>> res;
        for (int i = 0; i < n; i++) {
            if (same(x, i)) res.emplace_back(i, potential[i]);
        }
        return res;
    }
    vector<vector<pair<int, T>>> groups() {
        vector<vector<pair<int, T>>> res(n);
        for (int i = 0; i < n; i++) {
            res[root(i)].emplace_back(i, potential[i]);
        }
        res.erase(remove_if(res.begin(), res.end(), [&](const vector<pair<int, T>>& v) { return v.empty(); }), res.end());
        return res;
    }
};

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);
    int N, Q;
    cin >> N >> Q;

    PotentializedUnionFind<int> uf(N);
    while (Q--) {
        int t;
        cin >> t;
        if (t == 0) {
            int x, y, z;
            cin >> x >> y >> z;
            uf.merge(x, y, z);
        }
        if (t == 1) {
            int x, y;
            cin >> x >> y;
            if (uf.same(x, y)) {
                cout << uf.diff(x, y) << '\n';
            } else {
                cout << "?\n";
            }
        }
    }
}
accuracy: 1
time_ms: 20
memory_kb: 4096
final_rank: 1

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define ll long long
// #define rep(i,n) for(int i=0;i<(int)n;i++)
#define rep(i,n) for(ll i=0;i<(ll)n;i++)
#define vi vector<int>
#define vl vector<ll>
#define vd vector<double>
#define vb vector<bool>
#define vs vector<string>
#define vc vector<char>
#define ull unsigned long long
#define chmax(a,b) a=max(a,b)
#define chmin(a,b) a=min(a,b)



ll inf=(1ll<<60);

// ll rui(ll a,ll b){
//     if(b==0)return 1;
//     if(b%2==1) return a*rui(a*a,b/2);
//     return rui(a*a,b/2);
// }

// ll kai(ll n){
//     if(n==0)return 1;
//     return n*kai(n-1);
// }

// ll mod=?;
// ll const mod=998244353ll;
// ll modrui(ll a,ll b){
//     if(b==0)return 1;
//     if(b%2==1) return a%mod*modrui(a%mod*a%mod,b/2)%mod;
//     return modrui(a%mod*a%mod,b/2)%mod;
// }

// ll inv(ll x){
//     return modrui(x,mod-2);
// }

// ll modkai(ll n){
//     ll ret=1;
//     rep(i,n){
//         ret*=i+1;
//         ret%=mod;
//     }
//     return ret;
// }


// void incr(vl &f,ll &n){
//     ll k=f.size();
//     f[k-1]++;
//     ll now=k-1;
//     while (f[now]>=n)
//     {
//         f[now]=0;
//         if(now==0)break;
//         f[now-1]++;
//         now--;
//     }
//     return;
// }




// weighted unionfind
struct WeightedUnionFind {
    vector<ll> par, rank, diff_weight;

    WeightedUnionFind(ll n) : par(n), rank(n, 0), diff_weight(n, 0) {
        for (ll i = 0; i < n; i++) par[i] = i;
    }

    ll find(ll x) {
        if (par[x] == x) return x;
        ll r = find(par[x]);
        diff_weight[x] += diff_weight[par[x]];
        return par[x] = r;
    }

    ll weight(ll x) {
        find(x);
        return diff_weight[x];
    }

    bool unite(ll x, ll y, ll w) {// a[y] - a[x] = w
        w += weight(x);
        w -= weight(y);
        x = find(x);
        y = find(y);
        if (x == y) return false;
        if (rank[x] < rank[y]) swap(x, y), w = -w;
        if (rank[x] == rank[y]) rank[x]++;
        par[y] = x;
        diff_weight[y] = w;
        return true;
    }

    bool same(ll x, ll y) {
        return find(x) == find(y);
    }

    ll diff(ll x, ll y) {// return a[y]-a[x]
        return weight(y) - weight(x);
    }
};




void solve(){
    ll n,q,x,y,z,op;
    cin >> n >> q;
    WeightedUnionFind uf(n);
    while(q--){
        cin >> op;
        if(op==0){
            cin >> x >> y >> z;
            uf.unite(x,y,z);
        }
        else{
            cin >> x >> y;
            if(uf.same(x,y))cout << uf.diff(x,y) << endl;
            else cout << "?\n";
        }
    }
}


int main(){
    ll t=1;
    // cin >> t;
    while(t--)solve();
}
accuracy: 1
time_ms: 160
memory_kb: 5464
final_rank: 19

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define ll long long
// #define rep(i,n) for(int i=0;i<(int)n;i++)
#define rep(i,n) for(ll i=0;i<(ll)n;i++)
#define vi vector<int>
#define vl vector<ll>
#define vd vector<double>
#define vb vector<bool>
#define vs vector<string>
#define vc vector<char>
#define ull unsigned long long
#define chmax(a,b) a=max(a,b)
#define chmin(a,b) a=min(a,b)



ll inf=(1ll<<60);

// ll rui(ll a,ll b){
//     if(b==0)return 1;
//     if(b%2==1) return a*rui(a*a,b/2);
//     return rui(a*a,b/2);
// }

// ll kai(ll n){
//     if(n==0)return 1;
//     return n*kai(n-1);
// }

// ll mod=?;
// ll const mod=998244353ll;
// ll modrui(ll a,ll b){
//     if(b==0)return 1;
//     if(b%2==1) return a%mod*modrui(a%mod*a%mod,b/2)%mod;
//     return modrui(a%mod*a%mod,b/2)%mod;
// }

// ll inv(ll x){
//     return modrui(x,mod-2);
// }

// ll modkai(ll n){
//     ll ret=1;
//     rep(i,n){
//         ret*=i+1;
//         ret%=mod;
//     }
//     return ret;
// }


// void incr(vl &f,ll &n){
//     ll k=f.size();
//     f[k-1]++;
//     ll now=k-1;
//     while (f[now]>=n)
//     {
//         f[now]=0;
//         if(now==0)break;
//         f[now-1]++;
//         now--;
//     }
//     return;
// }




// weighted unionfind
struct WeightedUnionFind {
    vector<ll> par, rank, diff_weight;

    WeightedUnionFind(ll n) : par(n), rank(n, 0), diff_weight(n, 0) {
        for (ll i = 0; i < n; i++) par[i] = i;
    }

    ll find(ll x) {
        if (par[x] == x) return x;
        ll r = find(par[x]);
        diff_weight[x] += diff_weight[par[x]];
        return par[x] = r;
    }

    ll weight(ll x) {
        find(x);
        return diff_weight[x];
    }

    bool unite(ll x, ll y, ll w) {// a[x] - a[y] = w
        w += weight(x);
        w -= weight(y);
        x = find(x);
        y = find(y);
        if (x == y) return false;
        if (rank[x] < rank[y]) swap(x, y), w = -w;
        if (rank[x] == rank[y]) rank[x]++;
        par[y] = x;
        diff_weight[y] = w;
        return true;
    }

    bool same(ll x, ll y) {
        return find(x) == find(y);
    }

    ll diff(ll x, ll y) {// return a[y]-a[x]
        return weight(y) - weight(x);
    }
};




void solve(){
    ll n,q,x,y,z,op;
    cin >> n >> q;
    WeightedUnionFind uf(n);
    while(q--){
        cin >> op;
        if(op==0){
            cin >> x >> y >> z;
            uf.unite(x,y,z);
        }
        else{
            cin >> x >> y;
            if(uf.same(x,y))cout << uf.diff(x,y) << endl;
            else cout << "?\n";
        }
    }
}


int main(){
    ll t=1;
    // cin >> t;
    while(t--)solve();
}
accuracy: 1
time_ms: 160
memory_kb: 5492
final_rank: 20

code_snippet: #include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
using namespace std;
using ll = long long;
constexpr ll INF = 1LL << 60;
template <class Type>
struct WeightedUnionFind {
    vector<int> par, size;
    vector<Type> diffWeight;

    WeightedUnionFind(int n) : par(n), size(n, 1), diffWeight(n) {
        iota(par.begin(), par.end(), 0);
    }

    int root(int x) {
        if (par[x] == x) return x;
        int r = root(par[x]);
        diffWeight[x] += diffWeight[par[x]];
        return par[x] = r;
    }

    Type weight(int x) {
        root(x);
        return diffWeight[x];
    }

    bool merge(int x, int y, Type w) {
        w += weight(x);
        w -= weight(y);
        x = root(x);
        y = root(y);
        if (x == y) return false;
        if (size[x] < size[y]) {
            swap(x, y);
            w = -w;
        }
        par[y] = x;
        size[x] += size[y];
        diffWeight[y] = w;
        return true;
    }

    bool isSame(int x, int y) {
        return root(x) == root(y);
    }

    Type diff(int x, int y) {
        return weight(y) - weight(x);
    }

    int groupSize(int x) {
        return size[root(x)];
    }
};
int main() {
    int n,m;
    cin >>n >> m;
    WeightedUnionFind<int> wuf(n);
    rep(i,m) {
        int t;
        cin >> t;
        if (t == 0) {
            int x,y,d;
            cin >> x >> y >> d;
            wuf.merge(x,y,d);

        }
        else {
            int x,y;
            cin >> x >> y;
            if (!wuf.isSame(x,y)) {
                cout << "?" << endl;
                continue;
            }
            cout << wuf.diff(x,y) << endl;
        }
    }  
    
    return 0;
}
accuracy: 1
time_ms: 160
memory_kb: 4168
final_rank: 11

code_snippet: #include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define fr(a, b) for (int i = a; i < b; i++)
#define rep(i, a, b) for (int i = a; i < b; i++)
#define mod 1000000007
#define inf (1LL << 60)
#define all(x) (x).begin(), (x).end()
#define prDouble(x) cout << fixed << setprecision(10) << x
#define triplet pair<ll, pair<ll, ll>>
#define goog(tno) cout << "Case #" << tno << ": "
#define fast_io                                                                \
    ios_base::sync_with_stdio(false);                                          \
    cin.tie(NULL);                                                             \
    cout.tie(NULL);
#define read(x)                                                                \
    int x;                                                                     \
    cin >> x
using namespace std;

struct WeightedDSU {
    int n, set_size, *parent, *value, *rank;
    vector<vector<int>> graph;

    WeightedDSU(int a) {
        n = a;
        set_size = a;
        parent = new int[n];
        value = new int[n];
        rank = new int[n];
        graph.resize(n);
        for (int i = 0; i < n; i++) {
            parent[i] = i;
            rank[i] = 1;
            value[i] = 0;
        }
    }

    int find(int a) {
        if (parent[a] != a)
            return parent[a] = find(parent[a]);

        return parent[a];
    }

    void dfs(int a, int par, int val) {
        value[a] += val;

        for (int u : graph[a]) {
            if (u == par)
                continue;
            dfs(u, a, val);
        }
    }

    void merge(int a, int b, int wt) {
        int aroot = find(a);
        int broot = find(b);

        if (aroot != broot) {
            if (rank[aroot] >= rank[broot]) {
                int val = value[a] - value[b];
                val -= wt;
                dfs(broot, -1, val);
                parent[broot] = aroot;
                rank[aroot] += rank[broot];
            } else {
                int val = value[b] - value[a];
                val += wt;
                dfs(aroot, -1, val);
                parent[aroot] = broot;
                rank[broot] += rank[aroot];
            }
            graph[a].push_back(b);
            graph[b].push_back(a);
        }
    }
    int value1(int x, int y) {
        if (find(x) != find(y)) // Not connected components.
            return 1e9;
        return value[x] - value[y];
    }
};

int main() {
    fast_io int t = 1;
    // read(t);
    while (t--) {
        // Your code here
        read(n);
        read(q);
        WeightedDSU uf(n);

        while (q--) {
            int type;
            cin >> type;
            if (type == 0) {
                int a, b, c;
                cin >> a >> b >> c;
                // edge.push_back({c, {a, b}});
                uf.merge(a, b, c);
            } else {
                int a, b;
                cin >> a >> b;
                int x = uf.value1(a, b);
                if (x == 1e9)
                    cout << "?" << "\n";
                else
                    cout << x << "\n";
            }
        }
    }
    return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 9528
final_rank: 17

code_snippet: /******************************************************************************
 * Potential-Difference Union-Find
 *
 * Problem statement
 * ---------------------------------------------------------------------------
 * You are given `n` variables (0-indexed) whose integer values are initially
 * unknown.  You must process `q` online operations of two kinds:
 *
 *   0 x y z   —  Add the constraint      value[x] − value[y] = z
 *                (if x and y are already connected, the equation is guaranteed
 *                to be consistent with the previous ones).
 *
 *   1 x y     —  If x and y belong to the same connected component, output
 *                the implied difference  value[x] − value[y] .
 *                Otherwise output a single question-mark “?”.
 *
 * Input format
 * ---------------------------------------------------------------------------
 *   n q
 *   q lines, each in one of the formats above.
 *
 * Output
 * ---------------------------------------------------------------------------
 *   For every type-1 query, print either the integer difference or “?”.
 *
 * Constraints
 * ---------------------------------------------------------------------------
 *   1 ≤ n, q ≤ 200 000
 *   −10⁹ ≤ z ≤ 10⁹
 *
 * Solution outline
 * ---------------------------------------------------------------------------
 * Maintain a weighted Disjoint-Set Union (Union-Find) structure.
 * For each node we store:
 *   • parent index
 *   • `pot`  =  value[node] − value[parent]
 *
 * Taking the transitive sum of `pot` fields along the path to the root yields
 * the potential (value) difference with respect to that root.
 * Merging two components while inserting a new equation lets us fix the
 * relative potential between their roots and keeps future queries consistent.
 *
 * The implementation below runs in O((n+q) α(n)) time and O(n) memory.
 ******************************************************************************/

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

#define F first
#define S second

struct WeightedDSU {
    vector<pair<int, long long>> par; // (parent, pot = val - parent_val)
    vector<int> sz;

    WeightedDSU(int n = 0) { init(n); }

    void init(int n) {
        par.resize(n);
        sz.assign(n, 1);
        for (int i = 0; i < n; ++i)
            par[i] = {i, 0};
    }

    // Find root of x, compress path and return (root, potential wrt root)
    pair<int, long long> find(int x) {
        if (par[x].F == x)
            return par[x];
        auto [r, p] = find(par[x].F);
        long long newPot = p + par[x].S;
        return par[x] = {r, newPot};
    }

    // Enforce value[x] - value[y] = diff
    void unite(int x, int y, long long diff) {
        auto [rx, px] = find(x);
        auto [ry, py] = find(y);
        if (rx == ry)
            return; // already consistent

        // Make ry the larger set’s root
        if (sz[rx] > sz[ry]) {
            swap(rx, ry);
            swap(px, py);
            diff = -diff; // direction changed
        }

        // Attach rx under ry and set its pot so that equation holds:
        // value[x] = value[rx] + (px)
        // value[y] = value[ry] + (py)
        // We need: (value[x] - value[y]) == diff
        // → (value[rx] + px) - (value[ry] + py) = diff
        // → value[rx] - value[ry] = diff - (px - py)
        long long pot_rx_to_ry = diff - (px - py);
        par[rx] = {ry, pot_rx_to_ry};
        sz[ry] += sz[rx];
    }

    // Query value[x] - value[y]; must be in same component
    long long diff(int x, int y) { return find(x).S - find(y).S; }

    bool same(int x, int y) { return find(x).F == find(y).F; }
};

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

    int n, q;
    if (!(cin >> n >> q))
        return 0;

    WeightedDSU dsu(n);

    while (q--) {
        int type;
        cin >> type;
        if (type == 0) {
            int x, y;
            long long z;
            cin >> x >> y >> z; // enforce  value[x] - value[y] = z
            dsu.unite(y, x, z);
        } else { // type == 1
            int x, y;
            cin >> x >> y;
            if (!dsu.same(x, y))
                cout << "?\n";
            else
                cout << -dsu.diff(x, y) << '\n';
        }
    }
    return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 5196
final_rank: 5

code_snippet: /**
 * アルゴリズムイントロダクション章末問題 19-2「深さの決定」
 * （depth-determination problem)
 *
 * find_depth(v)：vが属する木におけるvの深さを返す
 * graft(r,v)：節点rを節点vの子にする。ただしrは木の根、vはrとは別の木の（根とは限らない）節点
 *
 */

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define all(x) (x).begin(), (x).end()
using ll = long long;
#ifdef LOCAL
#include <debug_print.hpp>
#define debug(...) debug_print::multi_print(#__VA_ARGS__, __VA_ARGS__)
#else
#define debug(...) (static_cast<void>(0))
#endif

class Dsu {
 public:
  int n;
  vector<int> leader;
  vector<int> parent;
  vector<int> rank;
  // 親ノードとの差分
  vector<int> diff_weight;

  Dsu() {}
  Dsu(int n_) : n(n_) {
    leader.resize(n);
    parent.resize(n);
    rank.resize(n);
    diff_weight.resize(n);
    rep(i, n) {
      leader[i] = i;
      parent[i] = i;
      rank[i] = 0;
      diff_weight[i] = 0;
    }
  }
  int get_leader(int x) {
    if (parent[x] == x) return x;
    int root = get_leader(parent[x]);
    diff_weight[x] += diff_weight[parent[x]];
    return parent[x] = root;
  }

  // weight(y) - weight(x) = wとなるようにマージする
  void unite(int x, int y, int w) {
    w = w + get_weight(x) - get_weight(y);

    int lx = get_leader(x);
    int ly = get_leader(y);
    assert(lx != ly);
    if (rank[lx] == rank[ly]) {
      parent[ly] = lx;
      rank[lx]++;
      diff_weight[ly] = w;
    } else if (rank[lx] > rank[ly]) {
      parent[ly] = lx;
      diff_weight[ly] = w;
    } else {
      parent[lx] = ly;
      diff_weight[lx] = -w;
    }
  }

  int get_weight(int x) {
    // マージ後の不整合を経路圧縮しながら解消
    get_leader(x);
    return diff_weight[x];
  }

  bool is_same(int x, int y) { return get_leader(x) == get_leader(y); }
};

void test_depth_determination_problem() {
  Dsu dsu(10);

  vector<tuple<int, int, int>> query;
  query = {{2, 0, 1}, {1, 0, -1}, {1, 1, -1}, {2, 3, 2},
           {2, 2, 0}, {1, 2, -1}, {1, 3, -1}};
  vector<int> expected = {1, 0, 2, 3};

  vector<int> actual;
  rep(qi, query.size()) {
    auto [com, v1, v2] = query[qi];
    if (com == 1) {
      dsu.get_leader(v1);
      actual.push_back(dsu.get_weight(v1));
    } else {
      dsu.unite(v2, v1, 1);
    }
    rep(i, 4) {
      //
      debug(i, dsu.get_weight(i));
    }
  }
  debug(actual);
  assert(expected == actual);
}

// https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=DSL_1_B&lang=ja
void solve_weighted_union_find() {
  int n, q;
  cin >> n >> q;
  Dsu dsu(n);

  while (q--) {
    int com;
    cin >> com;
    if (com == 0) {
      int x, y, z;
      cin >> x >> y >> z;
      if (!dsu.is_same(x, y)) {
        dsu.unite(x, y, z);
      }
    } else {
      int x, y;
      cin >> x >> y;
      if (dsu.is_same(x, y)) {
        cout << dsu.get_weight(y) - dsu.get_weight(x) << endl;
      } else {
        cout << "?" << endl;
      }
    }
  }
}
int main() {
  // test_depth_determination_problem();
  solve_weighted_union_find();
}
accuracy: 1
time_ms: 150
memory_kb: 4676
final_rank: 12