もちろん、この質問は練習をたくさん持っているが、私は誰もがDSUを書くことに驚いたん見ました
経験双方向側によるとさえ質問の前に、この問題は互いに素セットメンテナンスユニコムすることができません
そして、それぞれが尋ねるために\(X軸、Y軸の\)を、ヒューリスティックをマージ考えます
2つの設定点場合\(X、Y \)解くことができる、横断クエリ点に関連するいくつかは、マージ\(X、Yを\)クエリが実際に同等に設定され、それは直接インスピレーションを使用することができます私たちは、アレイを通過しなければならないとき、それは同時に、質問への答えのために行うことができ合併に関与ポイントのこのセットを保存することが合併のお問い合わせ
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=1e5+10;
int n,m,q;
struct Edge{
int u,v,x;
void Get(){ u=rd(),v=rd(),x=rd(); }
bool operator < (const Edge __) const {
return x>__.x;
}
}e[N];
int ans[N];
struct Query{
int x,id;
};
vector <Query> V[N];
int fa[N];
int Find(int x){ return fa[x]==x?x:fa[x]=Find(fa[x]);}
int main(){
n=rd(),m=rd();
rep(i,1,n) fa[i]=i;
rep(i,1,m) e[i].Get();
sort(e+1,e+m+1);
rep(i,1,q=rd()) {
ans[i]=-1;
int x=rd(),y=rd();
V[x].push_back((Query){y,i});
V[y].push_back((Query){x,i});
}
rep(i,1,m) {
int x=Find(e[i].u),y=Find(e[i].v);
if(x==y) continue;
if(V[x].size()>V[y].size()) swap(x,y);
fa[x]=y;
rep(j,0,V[x].size()-1) {
int t=V[x][j].x,id=V[x][j].id;
if(Find(t)==y) {
ans[id]=max(ans[id],e[i].x);
} else V[y].push_back(V[x][j]);
}
}
rep(i,1,q) printf("%d\n",ans[i]);
}
非常に簡潔な
あなたはヒューリスティックマージの原則を理解していない場合、私はそれは、単純な証明することができます
これらのセットの総数は、m個の要素
我々は、少なくとも二回大上述したサイズの小さなセットに併合うたびので、各要素は、合併のlog2で最もアクセス(M)倍になります
全体的に複雑\(Q \のCDOTログ(Q )\)