BZOJ3732 Network

<body> <center><h1>3732: Network</h1><span class="green">Time Limit: </span>10 Sec&nbsp;&nbsp;<span class="green">Memory Limit: </span>128 MB<br><span class="green">Submit: </span>3015&nbsp;&nbsp;<span class="green">Solved: </span>1489<br>[<a href="submitpage.php?id=3732">Submit</a>][<a href="problemstatus.php?id=3732">Status</a>][<a href="bbs.php?id=3732">Discuss</a>]</center><h2>Description</h2><div class="content"><p>给你N个点的无向图 (1 &lt;= N &lt;= 15,000),记为:1…N。 <br> 图中有M条边 (1 &lt;= M &lt;= 30,000) ,第j条边的长度为: d_j ( 1 &lt; = d_j &lt; = 1,000,000,000).</p> <p>现在有 K个询问 (1 &lt; = K &lt; = 20,000)。 <br> 每个询问的格式是:A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?</p></div><h2>Input</h2><div class="content"><p>第一行: N, M, K。 <br> 第2..M+1行: 三个正整数:X, Y, and D (1 &lt;= X &lt;=N; 1 &lt;= Y &lt;= N). 表示X与Y之间有一条长度为D的边。 <br> 第M+2..M+K+1行: 每行两个整数A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?</p></div><h2>Output</h2><div class="content"><p>&nbsp;对每个询问,输出最长的边最小值是多少。</p></div><h2>Sample Input</h2> <div class="content"><span class="sampledata">6 6 8<br> 1 2 5<br> 2 3 4<br> 3 4 3<br> 1 4 8<br> 2 5 7<br> 4 6 2<br> 1 2<br> 1 3<br> 1 4<br> 2 3<br> 2 4<br> 5 1<br> 6 2<br> 6 1</span></div><h2>Sample Output</h2> <div class="content"><span class="sampledata">5<br> 5<br> 5<br> 4<br> 4<br> 7<br> 4<br> 5</span></div><h2>HINT</h2> <div class="content"><p></p><p>1 &lt;= N &lt;= 15,000 <br><br> 1 &lt;= M &lt;= 30,000 <br><br> 1 &lt;= d_j &lt;= 1,000,000,000 <br><br> 1 &lt;= K &lt;= 15,000 </p><p></p></div><h2>Source</h2> <div class="content"><p><a href="problemset.php?search="></a></p></div><center>[<a href="submitpage.php?id=3732">Submit</a>][<a href="problemstatus.php?id=3732">Status</a>][<a href="bbs.php?id=3732">Discuss</a>]</center><br> <a href="./"><span class="red">HOME</span></a> <a href="javascript:history.go(-1)"><span class="red">Back</span></a> <hr> </body>

题解

参照自为风月马前卒的题解。

很显然答案一定在最小生成树上,但是此题还有一个更为玄学的做法—Kruskal重构树

它是在Kruskal算法上改进而来的。

算法流程:

  1. 对于此题来说,将边权从小到大排序
  2. 用并查集维护两点的联通性,若祖先不相同,那么新建一个节点,权值为边权。左右儿子分别为两个点

这样建出来的树,我们称之为Kruskal重构树,它有许多美妙的性质

  • 是一颗二叉树
  • 两点的LCA的点权为原图中最大值最小的路径上的最大值
  • 任意点的权值大于左右儿子的权值,是一个大根堆

对于此题的样例来说,建出来的图大概长这样

时间复杂度\(O(n \log n)\)


利用Kruskal重构树是棵二叉树,边表直接写成左右儿子。 ```cpp #include #define rg register #define il inline #define co const templateil T read(){ rg T data=0,w=1; rg char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') w=-1; ch=getchar(); } while(isdigit(ch)) data=data*10+ch-'0',ch=getchar(); return data*w; } templateil T read(rg T&x){ return x=read(); } typedef long long ll;

co int N=1e5;
int n,m,q;
struct edge {int u,v,w;}e[N];
bool operator<(co edge&a,co edge&b) {return a.w<b.w;}
int val[N],fa[N],ch[N][2],siz[N],son[N];
int dep[N],top[N],pos[N],dfn;
namespace DS{
int fa[N];
int find(int x) {return fa[x]x?x:fa[x]=find(fa[x]);}
}
void kruskal(){
for(int i=1;i<=n;++i) siz[i]=1,DS::fa[i]=i;
std::sort(e+1,e+m+1);
for(int i=1;i<=m;++i){
int x=DS::find(e[i].u),y=DS::find(e[i].v);
if(x
y) continue;
++n,val[n]=e[i].w;
fa[x]=n,fa[y]=n,ch[n][0]=x,ch[n][1]=y;
siz[n]=siz[x]+1+siz[y],son[n]=siz[x]>siz[y]?x:y;
DS::fa[n]=n,DS::fa[x]=n,DS::fa[y]=n;
}
}
void dfs(int x,int top){
dep[x]=dep[fa[x]]+1,::top[x]=top,pos[x]=++dfn;
if(!son[x]) return;
dfs(son[x],top);
if(ch[x][0]&&ch[x][0]!=son[x]) dfs(ch[x][0],ch[x][0]);
if(ch[x][1]&&ch[x][1]!=son[x]) dfs(ch[x][1],ch[x][1]);
}
int lca(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) std::swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
int main(){
// freopen(".in","r",stdin),freopen(".out","w",stdout);
read(n),read(m),read(q);
for(int i=1;i<=m;++i) read(e[i].u),read(e[i].v),read(e[i].w);
kruskal(),dfs(n,n);
while(q--) printf("%d\n",val[lca(read(),read())]);
return 0;
}

静渊以有谋,疏通而知事。

posted on 2019-03-17 16:16  autoint  阅读(82)  评论(0编辑  收藏  举报

导航