bzoj3732-Network
题意
给出一个 \(n\) 个点 \(m\) 条边的无向图,\(q\) 次询问 \((x,y)\) 的所有路径中最长边最小是多少。
\(n,m,q\le 3\times 10^4\)
分析
题意明显是最小生成树上两点之间最大边权。有三种做法。当模板题玩一玩就好了。
第一种是简单的最小生成树上倍增,复杂度为 \(O(n\log n)\) 。
第二种是最小生成树上用lct求最大边权,复杂度为 \(O(n\log n)\) 。
第三种是kruskal重构树上倍增lca,复杂度为 \(O(n\log n)\) 。
代码
这是第三种做法。
#include<bits/stdc++.h>
using namespace std;
inline char nchar() {
static const int bufl=1<<20;
static char buf[bufl],*a,*b;
return a==b && (b=(a=buf)+fread(buf,1,bufl,stdin),a==b)?EOF:*a++;
}
inline int read() {
int x=0,f=1;
char c=nchar();
for (;!isdigit(c);c=nchar()) if (c=='-') f=-1;
for (;isdigit(c);c=nchar()) x=x*10+c-'0';
return x*f;
}
const int maxn=3e4+1;
const int maxm=3e4+1;
const int maxj=15;
int n,m,q,tot;
struct edge {
int x,y,w;
inline bool operator < (const edge &a) const {return w<a.w;}
} e[maxm];
int f[maxn];
int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}
namespace tree {
vector<int> g[maxn];
int w[maxn],f[maxn][maxj],dep[maxn];
inline void add(int x,int y) {g[x].push_back(y);}
void dfs(int x,int fa) {
dep[x]=dep[fa]+1;
f[x][0]=fa;
for (int j=1;j<maxj;++j) f[x][j]=f[f[x][j-1]][j-1];
for (vector<int>::iterator it=g[x].begin();it!=g[x].end();++it) dfs(*it,x);
}
inline int lca(int x,int y) {
if (dep[x]<dep[y]) swap(x,y);
for (int j=maxj-1;j>=0;--j) if (dep[f[x][j]]>=dep[y]) x=f[x][j];
if (x==y) return x;
for (int j=maxj-1;j>=0;--j) if (f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j];
return f[x][0];
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
tot=n=read(),m=read(),q=read();
for (int i=1;i<=m;++i) {
int x=read(),y=read(),w=read();
e[i]=(edge){x,y,w};
}
for (int i=1;i<(n<<1);++i) f[i]=i;
sort(e+1,e+m+1);
for (int i=1;i<=m;++i) {
int &x=e[i].x,&y=e[i].y,&w=e[i].w;
int fx=find(x),fy=find(y);
if (fx!=fy) {
f[fx]=f[fy]=++tot;
tree::w[tot]=w;
tree::add(tot,fx),tree::add(tot,fy);
}
}
tree::dfs(tot,tot);
while (q--) {
int x=read(),y=read();
int ans=tree::w[tree::lca(x,y)];
printf("%d\n",ans);
}
return 0;
}