#define PROBLEM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=DSL_1_B"
#include"../../../ds/dsu/rollback_potentialized_dsu.hpp"#include<array>
#include<cassert>
#include<iostream>
#include<random>
#include<tuple>
#include<vector>#include"../../../monoid/add.hpp"namespace{usingAdd=m1une::monoid::Add<longlong>;usingDsu=m1une::ds::RollbackPotentializedDsu<Add>;structPermutationGroup{usingvalue_type=std::array<int,3>;staticvalue_typeid(){return{0,1,2};}staticvalue_typeop(constvalue_type&first,constvalue_type&second){value_typeresult;for(intindex=0;index<3;++index){result[index]=second[first[index]];}returnresult;}staticvalue_typeinv(constvalue_type&value){value_typeresult;for(intindex=0;index<3;++index)result[value[index]]=index;returnresult;}};voidnoncommutative_test(){usingPermutation=PermutationGroup::value_type;m1une::ds::RollbackPotentializedDsu<PermutationGroup>dsu(4);Permutationrotate={1,2,0};Permutationswap_last={0,2,1};assert(dsu.merge(0,1,rotate));intstate=dsu.snapshot();assert(dsu.merge(1,2,swap_last));Permutationcomposed=PermutationGroup::op(rotate,swap_last);assert(dsu.diff(0,2)==composed);assert(!dsu.merge(0,2,PermutationGroup::op(swap_last,rotate)));dsu.rollback(state);assert(!dsu.same(0,2));}voidrandomized_test(){constexprintsize=18;Dsudsu(size);std::vector<std::vector<longlong>>graph(size,std::vector<longlong>(size));std::vector<std::vector<bool>>edge(size,std::vector<bool>(size));std::vector<std::tuple<int,int,longlong>>history;std::mt19937random(0);autorebuild=[&]{graph.assign(size,std::vector<longlong>(size));edge.assign(size,std::vector<bool>(size));for(constauto&[first,second,difference]:history){edge[first][second]=edge[second][first]=true;graph[first][second]=difference;graph[second][first]=-difference;}};autonaive_potential=[&](intstart){std::vector<bool>seen(size);std::vector<longlong>potential(size);std::vector<int>stack={start};seen[start]=true;while(!stack.empty()){intvertex=stack.back();stack.pop_back();for(intnext=0;next<size;++next){if(!edge[vertex][next]||seen[next])continue;seen[next]=true;potential[next]=potential[vertex]+graph[vertex][next];stack.push_back(next);}}returnstd::pair(std::move(seen),std::move(potential));};autovalidate=[&]{for(intfirst=0;first<size;++first){auto[seen,potential]=naive_potential(first);for(intsecond=0;second<size;++second){assert(dsu.same(first,second)==seen[second]);if(seen[second]){assert(dsu.diff(first,second)==potential[second]);}}}};for(intround=0;round<120;++round){intstate=dsu.snapshot();std::size_thistory_size=history.size();intupdate_count=1+int(random()%8);for(intstep=0;step<update_count;++step){intfirst=int(random()%size);intsecond=int(random()%size);auto[seen,potential]=naive_potential(first);longlongdifference=seen[second]?potential[second]:static_cast<longlong>(int(random()%41)-20);boolconsistent=dsu.merge(first,second,difference);assert(consistent);if(!seen[second])history.emplace_back(first,second,difference);rebuild();validate();}dsu.rollback(state);history.resize(history_size);rebuild();validate();}}}// namespaceintmain(){noncommutative_test();randomized_test();intvertex_count,query_count;std::cin>>vertex_count>>query_count;Dsudsu(vertex_count);for(intquery=0;query<query_count;++query){inttype,first,second;std::cin>>type>>first>>second;if(type==0){longlongdifference;std::cin>>difference;dsu.merge(first,second,difference);}elseif(dsu.same(first,second)){std::cout<<dsu.diff(first,second)<<'\n';}else{std::cout<<"?\n";}}}
#line 1 "verify/ds/dsu/rollback_potentialized_dsu.test.cpp"
#define PROBLEM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=DSL_1_B"
#line 1 "ds/dsu/rollback_potentialized_dsu.hpp"
#include<algorithm>
#include<cassert>
#include<concepts>
#include<cstddef>
#include<utility>
#include<vector>#line 1 "monoid/concept.hpp"
#line 5 "monoid/concept.hpp"
namespacem1une{namespacemonoid{// Concept to check if a type satisfies the requirements of a Monoid.// A Monoid must have a `value_type`, an identity element `id()`, and an associative binary operation `op()`.template<typenameM>conceptIsMonoid=requires(typenameM::value_typea,typenameM::value_typeb){// 1. Must define `value_type`typenameM::value_type;// 2. Must have a static method `id()` returning `value_type`{M::id()}->std::same_as<typenameM::value_type>;// 3. Must have a static method `op(a, b)` returning `value_type`{M::op(a,b)}->std::same_as<typenameM::value_type>;};// Concept for groups. A type satisfying this concept must also obey the group// laws; concepts can check the interface but not the algebraic properties.template<typenameM>conceptIsGroup=IsMonoid<M>&&requires(typenameM::value_typea){{M::inv(a)}->std::same_as<typenameM::value_type>;};// Concept for commutative groups. Commutativity is a semantic requirement and// cannot be checked by a C++ concept.template<typenameM>conceptIsCommutativeGroup=IsGroup<M>;}// namespace monoid}// namespace m1une#line 12 "ds/dsu/rollback_potentialized_dsu.hpp"
namespacem1une{namespaceds{template<m1une::monoid::IsGroupGroup>requiresstd::equality_comparable<typenameGroup::value_type>structRollbackPotentializedDsu{usingT=typenameGroup::value_type;private:structHistoryEntry{intfirst;intfirst_value;intsecond;intsecond_value;Tsecond_diff;HistoryEntry(intfirst_index,intfirst_parent,intsecond_index,intsecond_parent,Tdiff):first(first_index),first_value(first_parent),second(second_index),second_value(second_parent),second_diff(std::move(diff)){}};int_n;int_component_count;std::vector<int>_parent_or_size;std::vector<T>_diff_to_parent;std::vector<HistoryEntry>_history;std::vector<std::size_t>_checkpoints;staticintcheck_size(intn){assert(0<=n);returnn;}std::pair<int,T>leader_and_potential(intvertex)const{assert(0<=vertex&&vertex<_n);Tresult=Group::id();while(_parent_or_size[vertex]>=0){result=Group::op(_diff_to_parent[vertex],result);vertex=_parent_or_size[vertex];}return{vertex,std::move(result)};}public:RollbackPotentializedDsu():RollbackPotentializedDsu(0){}explicitRollbackPotentializedDsu(intn):_n(check_size(n)),_component_count(_n),_parent_or_size(_n,-1),_diff_to_parent(_n,Group::id()){}intsize()const{return_n;}boolempty()const{return_n==0;}intcomponent_count()const{return_component_count;}intsnapshot_count()const{returnint(_checkpoints.size());}voidreserve_snapshots(intcount){assert(0<=count);_checkpoints.reserve(count);}intleader(intvertex)const{returnleader_and_potential(vertex).first;}boolsame(intfirst,intsecond)const{returnleader(first)==leader(second);}intgroup_size(intvertex)const{return-_parent_or_size[leader(vertex)];}intsize(intvertex)const{returngroup_size(vertex);}Tpotential(intvertex)const{returnleader_and_potential(vertex).second;}Tdiff(intfirst,intsecond)const{assert(same(first,second));returnGroup::op(Group::inv(potential(first)),potential(second));}intparent_or_size(intvertex)const{assert(0<=vertex&&vertex<_n);return_parent_or_size[vertex];}boolmerge(intfirst,intsecond,constT&difference){auto[first_root,first_potential]=leader_and_potential(first);auto[second_root,second_potential]=leader_and_potential(second);if(first_root==second_root){returnGroup::op(Group::inv(first_potential),second_potential)==difference;}Tsecond_from_first=Group::op(Group::op(first_potential,difference),Group::inv(second_potential));if(-_parent_or_size[first_root]<-_parent_or_size[second_root]){std::swap(first_root,second_root);second_from_first=Group::inv(second_from_first);}if(!_checkpoints.empty()){_history.emplace_back(first_root,_parent_or_size[first_root],second_root,_parent_or_size[second_root],_diff_to_parent[second_root]);}_parent_or_size[first_root]+=_parent_or_size[second_root];_parent_or_size[second_root]=first_root;_diff_to_parent[second_root]=std::move(second_from_first);--_component_count;returntrue;}private:voidrestore_one(){HistoryEntryentry=std::move(_history.back());_history.pop_back();_parent_or_size[entry.first]=entry.first_value;_parent_or_size[entry.second]=entry.second_value;_diff_to_parent[entry.second]=std::move(entry.second_diff);++_component_count;}public:intsnapshot(){_checkpoints.push_back(_history.size());returnint(_checkpoints.size());}voidrollback(intstate){assert(1<=state&&state<=snapshot_count());while(_history.size()>_checkpoints[state-1])restore_one();_checkpoints.resize(state);}voidclear_history(){_history.clear();_checkpoints.clear();}std::vector<std::vector<int>>groups()const{std::vector<int>leaders(_n);std::vector<int>sizes(_n);for(intvertex=0;vertex<_n;++vertex){leaders[vertex]=leader(vertex);++sizes[leaders[vertex]];}std::vector<std::vector<int>>result(_n);for(intvertex=0;vertex<_n;++vertex){result[vertex].reserve(sizes[vertex]);}for(intvertex=0;vertex<_n;++vertex){result[leaders[vertex]].push_back(vertex);}result.erase(std::remove_if(result.begin(),result.end(),[](conststd::vector<int>&group){returngroup.empty();}),result.end());returnresult;}};}// namespace ds}// namespace m1une#line 4 "verify/ds/dsu/rollback_potentialized_dsu.test.cpp"
#include<array>
#line 7 "verify/ds/dsu/rollback_potentialized_dsu.test.cpp"
#include<iostream>
#include<random>
#include<tuple>
#line 11 "verify/ds/dsu/rollback_potentialized_dsu.test.cpp"
#line 1 "monoid/add.hpp"
namespacem1une{namespacemonoid{// Monoid for addition (Range Sum).template<typenameT>structAdd{usingvalue_type=T;staticconstexprboolcommutative=true;// Returns the identity element for addition, which is 0.staticconstexprTid(){returnT(0);}// Returns the sum of a and b.staticconstexprTop(constT&a,constT&b){returna+b;}staticconstexprTinv(constT&x){return-x;}};}// namespace monoid}// namespace m1une#line 13 "verify/ds/dsu/rollback_potentialized_dsu.test.cpp"
namespace{usingAdd=m1une::monoid::Add<longlong>;usingDsu=m1une::ds::RollbackPotentializedDsu<Add>;structPermutationGroup{usingvalue_type=std::array<int,3>;staticvalue_typeid(){return{0,1,2};}staticvalue_typeop(constvalue_type&first,constvalue_type&second){value_typeresult;for(intindex=0;index<3;++index){result[index]=second[first[index]];}returnresult;}staticvalue_typeinv(constvalue_type&value){value_typeresult;for(intindex=0;index<3;++index)result[value[index]]=index;returnresult;}};voidnoncommutative_test(){usingPermutation=PermutationGroup::value_type;m1une::ds::RollbackPotentializedDsu<PermutationGroup>dsu(4);Permutationrotate={1,2,0};Permutationswap_last={0,2,1};assert(dsu.merge(0,1,rotate));intstate=dsu.snapshot();assert(dsu.merge(1,2,swap_last));Permutationcomposed=PermutationGroup::op(rotate,swap_last);assert(dsu.diff(0,2)==composed);assert(!dsu.merge(0,2,PermutationGroup::op(swap_last,rotate)));dsu.rollback(state);assert(!dsu.same(0,2));}voidrandomized_test(){constexprintsize=18;Dsudsu(size);std::vector<std::vector<longlong>>graph(size,std::vector<longlong>(size));std::vector<std::vector<bool>>edge(size,std::vector<bool>(size));std::vector<std::tuple<int,int,longlong>>history;std::mt19937random(0);autorebuild=[&]{graph.assign(size,std::vector<longlong>(size));edge.assign(size,std::vector<bool>(size));for(constauto&[first,second,difference]:history){edge[first][second]=edge[second][first]=true;graph[first][second]=difference;graph[second][first]=-difference;}};autonaive_potential=[&](intstart){std::vector<bool>seen(size);std::vector<longlong>potential(size);std::vector<int>stack={start};seen[start]=true;while(!stack.empty()){intvertex=stack.back();stack.pop_back();for(intnext=0;next<size;++next){if(!edge[vertex][next]||seen[next])continue;seen[next]=true;potential[next]=potential[vertex]+graph[vertex][next];stack.push_back(next);}}returnstd::pair(std::move(seen),std::move(potential));};autovalidate=[&]{for(intfirst=0;first<size;++first){auto[seen,potential]=naive_potential(first);for(intsecond=0;second<size;++second){assert(dsu.same(first,second)==seen[second]);if(seen[second]){assert(dsu.diff(first,second)==potential[second]);}}}};for(intround=0;round<120;++round){intstate=dsu.snapshot();std::size_thistory_size=history.size();intupdate_count=1+int(random()%8);for(intstep=0;step<update_count;++step){intfirst=int(random()%size);intsecond=int(random()%size);auto[seen,potential]=naive_potential(first);longlongdifference=seen[second]?potential[second]:static_cast<longlong>(int(random()%41)-20);boolconsistent=dsu.merge(first,second,difference);assert(consistent);if(!seen[second])history.emplace_back(first,second,difference);rebuild();validate();}dsu.rollback(state);history.resize(history_size);rebuild();validate();}}}// namespaceintmain(){noncommutative_test();randomized_test();intvertex_count,query_count;std::cin>>vertex_count>>query_count;Dsudsu(vertex_count);for(intquery=0;query<query_count;++query){inttype,first,second;std::cin>>type>>first>>second;if(type==0){longlongdifference;std::cin>>difference;dsu.merge(first,second,difference);}elseif(dsu.same(first,second)){std::cout<<dsu.diff(first,second)<<'\n';}else{std::cout<<"?\n";}}}