classIntegerSumRuleUnionFind{usingll=longlong;intn,num;std::vector<int>sz,parent;std::vector<std::pair<int,ll>>potential;std::vector<std::optional<ll>>value;public:IntegerSumRuleUnionFind()=default;IntegerSumRuleUnionFind(intn):n(n),num(n),sz(n,1),parent(n,0),potential(n,{1,0}),value(n,std::nullopt){std::iota(parent.begin(),parent.end(),0);}std::tuple<int,int,ll>from_root(intx){if(x==parent[x])return{x,1,0LL};auto[r,a,b]=from_root(parent[x]);auto[c,d]=potential[x];parent[x]=r;potential[x]={a*c,b*c+d};return{r,a*c,b*c+d};}intleader(intx){returnget<0>(from_root(x));}boolsame(intx,inty){assert(0<=xandx<nand0<=yandy<n);returnleader(x)==leader(y);}boolmerge(intx,inty,llsum){// 矛盾する場合は変更はせず false を返すassert(0<=xandx<nand0<=yandy<n);auto[rx,a,b]=from_root(x);auto[ry,c,d]=from_root(y);if(rx==ry){// ar+b + cr+d = sumif(a==-c)returnb+d==sum;if((sum-b-d)&1)returnfalse;llr=(sum-b-d)/(a+c);if(value[rx]andvalue[rx].value()!=r)returnfalse;// これ起きる?value[rx]=r;returntrue;}if(sz[rx]<sz[ry]){std::swap(rx,ry);std::swap(a,c);std::swap(b,d);}// a * rx + b + c * ry + d == sum// rx = -c/a ry + (sum-b-d)/a// ry = -a/c rx + (sum-b-d)/cif(value[ry]){llk=-c*a*value[ry].value()+(sum-b-d)*a;if(value[rx]andvalue[rx].value()!=k)returnfalse;value[rx]=k;}sz[rx]+=sz[ry];parent[ry]=rx;potential[ry]={-a*c,(sum-b-d)*c};num--;returntrue;}std::optional<ll>val(intx){auto[r,a,b]=from_root(x);if(value[r])returnvalue[r].value()*a+b;returnstd::nullopt;}// x と y が隣接してないなら std::nullopt// x と y が隣接しているが、sum が一意でない場合も std::nulloptstd::optional<ll>sum(intx,inty){auto[rx,a,b]=from_root(x);auto[ry,c,d]=from_root(y);if(rx!=ry)returnstd::nullopt;if(a==c){assert(b==d);returnstd::nullopt;}returnb+d;}intsize(constintx){assert(0<=xandx<n);returnsz[leader(x)];}intcount()const{returnnum;}};
#line 1 "library/datastructure/unionfind/IntegerSumRuleUnionFind.hpp"
classIntegerSumRuleUnionFind{usingll=longlong;intn,num;std::vector<int>sz,parent;std::vector<std::pair<int,ll>>potential;std::vector<std::optional<ll>>value;public:IntegerSumRuleUnionFind()=default;IntegerSumRuleUnionFind(intn):n(n),num(n),sz(n,1),parent(n,0),potential(n,{1,0}),value(n,std::nullopt){std::iota(parent.begin(),parent.end(),0);}std::tuple<int,int,ll>from_root(intx){if(x==parent[x])return{x,1,0LL};auto[r,a,b]=from_root(parent[x]);auto[c,d]=potential[x];parent[x]=r;potential[x]={a*c,b*c+d};return{r,a*c,b*c+d};}intleader(intx){returnget<0>(from_root(x));}boolsame(intx,inty){assert(0<=xandx<nand0<=yandy<n);returnleader(x)==leader(y);}boolmerge(intx,inty,llsum){// 矛盾する場合は変更はせず false を返すassert(0<=xandx<nand0<=yandy<n);auto[rx,a,b]=from_root(x);auto[ry,c,d]=from_root(y);if(rx==ry){// ar+b + cr+d = sumif(a==-c)returnb+d==sum;if((sum-b-d)&1)returnfalse;llr=(sum-b-d)/(a+c);if(value[rx]andvalue[rx].value()!=r)returnfalse;// これ起きる?value[rx]=r;returntrue;}if(sz[rx]<sz[ry]){std::swap(rx,ry);std::swap(a,c);std::swap(b,d);}// a * rx + b + c * ry + d == sum// rx = -c/a ry + (sum-b-d)/a// ry = -a/c rx + (sum-b-d)/cif(value[ry]){llk=-c*a*value[ry].value()+(sum-b-d)*a;if(value[rx]andvalue[rx].value()!=k)returnfalse;value[rx]=k;}sz[rx]+=sz[ry];parent[ry]=rx;potential[ry]={-a*c,(sum-b-d)*c};num--;returntrue;}std::optional<ll>val(intx){auto[r,a,b]=from_root(x);if(value[r])returnvalue[r].value()*a+b;returnstd::nullopt;}// x と y が隣接してないなら std::nullopt// x と y が隣接しているが、sum が一意でない場合も std::nulloptstd::optional<ll>sum(intx,inty){auto[rx,a,b]=from_root(x);auto[ry,c,d]=from_root(y);if(rx!=ry)returnstd::nullopt;if(a==c){assert(b==d);returnstd::nullopt;}returnb+d;}intsize(constintx){assert(0<=xandx<n);returnsz[leader(x)];}intcount()const{returnnum;}};