#pragma once
// https://tiramister.net/blog/posts/persistent-unionfind/classPartialPersistentUnionFind{intnow;// 現在時刻std::vector<int>par,rank,time;std::vector<std::vector<std::pair<int,int>>>sz;staticconstexprintNOW=std::numeric_limits<int>::max();public:PartialPersistentUnionFind(intn):now(0),par(n),rank(n,0),time(n,0),sz(n){std::iota(par.begin(),par.end(),0);}// 時刻 t の leaderintfind(intx,intt=NOW){while(x!=par[x]andtime[x]<t)x=par[x];returnx;}// 時刻 t で x,y が連結かboolsame(intx,inty,intt=NOW){returnfind(x,t)==find(y,t);}// x と y がいつ連結になったか(まだ非連結なら -1 )// stack を使う実装も考えたけど少し遅そう atcoder/submissions/37116694intwhen_same(intx,inty){intdiff=0;// x の深さ - y の深さintX=x,Y=y;while(par[x]!=x){x=par[x];diff++;}while(par[y]!=y){y=par[y];diff--;}if(x!=y)return-1;intres=0;while(X!=Y){if(diff>0){res=std::max(res,time[X]);X=par[X];diff--;}else{res=std::max(res,time[Y]);Y=par[Y];diff++;}}returnres;}// merge が成功したら時刻を 1 進めたあとその時刻を返す// 失敗したら stop が false なら時刻を進めて、-1 を返すintmerge(intx,inty,boolstop=false){now++;x=find(x);y=find(y);if(x==y){if(stop)now--;return-1;}if(rank[x]<rank[y])std::swap(x,y);sz[x].emplace_back(now,size(x)+size(y));if(rank[x]==rank[y])rank[x]++;par[y]=x;returntime[y]=now;}// 時刻 t の連結成分 x のサイズintsize(intx,intt=NOW){x=find(x,t);intok=-1,ng=sz[x].size();while(ng-ok>1){intmid=(ok+ng)>>1;(sz[x][mid].first<=t?ok:ng)=mid;}return(~ok?sz[x][ok].second:1);}};
#line 2 "library/datastructure/unionfind/PartialPersistentUnionFind.hpp"
// https://tiramister.net/blog/posts/persistent-unionfind/classPartialPersistentUnionFind{intnow;// 現在時刻std::vector<int>par,rank,time;std::vector<std::vector<std::pair<int,int>>>sz;staticconstexprintNOW=std::numeric_limits<int>::max();public:PartialPersistentUnionFind(intn):now(0),par(n),rank(n,0),time(n,0),sz(n){std::iota(par.begin(),par.end(),0);}// 時刻 t の leaderintfind(intx,intt=NOW){while(x!=par[x]andtime[x]<t)x=par[x];returnx;}// 時刻 t で x,y が連結かboolsame(intx,inty,intt=NOW){returnfind(x,t)==find(y,t);}// x と y がいつ連結になったか(まだ非連結なら -1 )// stack を使う実装も考えたけど少し遅そう atcoder/submissions/37116694intwhen_same(intx,inty){intdiff=0;// x の深さ - y の深さintX=x,Y=y;while(par[x]!=x){x=par[x];diff++;}while(par[y]!=y){y=par[y];diff--;}if(x!=y)return-1;intres=0;while(X!=Y){if(diff>0){res=std::max(res,time[X]);X=par[X];diff--;}else{res=std::max(res,time[Y]);Y=par[Y];diff++;}}returnres;}// merge が成功したら時刻を 1 進めたあとその時刻を返す// 失敗したら stop が false なら時刻を進めて、-1 を返すintmerge(intx,inty,boolstop=false){now++;x=find(x);y=find(y);if(x==y){if(stop)now--;return-1;}if(rank[x]<rank[y])std::swap(x,y);sz[x].emplace_back(now,size(x)+size(y));if(rank[x]==rank[y])rank[x]++;par[y]=x;returntime[y]=now;}// 時刻 t の連結成分 x のサイズintsize(intx,intt=NOW){x=find(x,t);intok=-1,ng=sz[x].size();while(ng-ok>1){intmid=(ok+ng)>>1;(sz[x][mid].first<=t?ok:ng)=mid;}return(~ok?sz[x][ok].second:1);}};