【NOIP2013/Codevs3287】货车运输-最小生成树(大)-树上倍增
Problem 树上倍增
题目大意
给出一个图,给出若干个点对u,v,求u,v的一条路径,该路径上最小的边权值最大。
Solution
看到这个题第一反应是图论。。
然而,任意路径最小的边权值最大,如果仔细思考的话就会知道,如果两个点相互连通,那么一定走的是最大生成树上的路径,而不会选择其他任何一条路径去走。
这个是可以非常简单证明的,就不再详述。
那么既然知道了这个,当然是先建一颗最大生成树啦!
现在问题来了,Prim&Kruskal,选哪个?
分析一下,prim复杂度$O(n^2)$,n为总点数。
Kruskal复杂度$O(m\log_2n)$,m为总边数。
显而易见,在这一道题目中kruskal更优。
于是写一个kruskal最大生成树。
接下来要在这颗树上跑。
我们设立一个fa数组,其fa[i][j]表示对于i节点,向上的2^j个节点编号是什么。显而易见,fa[i][0]就是i的父亲。
$$fa[i][j]=fa[fa[i][j-1][j-1]$$
然后我们还需要一个储存最小值的数组,设立minn数组,其中minn[i][j]表示对于i节点,向上2^j个节点的边最小值
显而易见,minn[i][0]就表示i节点本身链接父亲边的权值.
$$minn[i][j]=\min(minn[fa[i][j-1]][j-1],minn[i][j-1])$$
可以看出,这两个数组在O(n)的时间就可以求出来了。
接下来,对于每一个询问点对,我们只需要倍增求lca,再求两个点到lca路径上最小值,就可以求出答案。
判断uv谁深度更深,更深深度先跳到同一深度。
接下来两个一起向上跳,能够跳就跳。
具体方法可以看代码。
AC Code
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 struct kruskal{ 7 int u,v,w; 8 }ekr[50010]; 9 struct node{ 10 int to,next,w; 11 }e[20010]; 12 int f[10010],h[10010],dep[10010],n,m,u,v,w,q,ktot=0,tot=0,ans; 13 int fa[10010][22],minn[10010][22]; 14 void add_kruskal(int u,int v,int w){ 15 ekr[++ktot].u=u;ekr[ktot].v=v;ekr[ktot].w=w; 16 } 17 bool cmp(kruskal a,kruskal b){ 18 return a.w>b.w; 19 } 20 void add(int u,int v,int w){ 21 e[++tot].to=v;e[tot].next=h[u];h[u]=tot;e[tot].w=w; 22 e[++tot].to=u;e[tot].next=h[v];h[v]=tot;e[tot].w=w; 23 } 24 void initdfs(int x,int last,int we,int depth){ 25 dep[x]=depth; 26 fa[x][0]=last; 27 minn[x][0]=we; 28 for(int i=1;i<=14;i++){ 29 fa[x][i]=fa[fa[x][i-1]][i-1]; 30 minn[x][i]=min(minn[x][i-1],minn[fa[x][i-1]][i-1]); 31 } 32 for(int i=h[x];~i;i=e[i].next) 33 if(e[i].to!=last)initdfs(e[i].to,x,e[i].w,depth+1); 34 } 35 void queue(int u,int v){ 36 if(dep[u]<dep[v])swap(u,v); 37 int dist=dep[u]-dep[v],tmp=0; 38 while(dist){ 39 if(dist%2==1)ans=min(ans,minn[u][tmp]),u=fa[u][tmp]; 40 tmp++; 41 dist>>=1; 42 } 43 for(int i=14;i>=0;i--){ 44 if(fa[u][i]!=fa[v][i]){ 45 ans=min(min(ans,minn[u][i]),minn[v][i]); 46 u=fa[u][i]; 47 v=fa[v][i]; 48 } 49 } 50 ans=(u==v)?ans:min(min(ans,minn[u][0]),minn[v][0]); 51 } 52 int find(int x){ 53 if(f[x]!=x)f[x]=find(f[x]); 54 return f[x]; 55 } 56 int main(){ 57 // freopen("xsy2018.in","r",stdin); 58 memset(h,-1,sizeof(h)); 59 scanf("%d%d",&n,&m); 60 for(int i=1;i<=m;i++){ 61 scanf("%d%d%d",&u,&v,&w); 62 add_kruskal(u,v,w); 63 } 64 sort(ekr+1,ekr+ktot+1,cmp); 65 for(int i=1;i<=n;i++)f[i]=i; 66 for(int i=1,sum=0;i<=ktot;i++){ 67 int fu=find(ekr[i].u),fv=find(ekr[i].v); 68 if(fu!=fv){ 69 add(ekr[i].u,ekr[i].v,ekr[i].w); 70 f[fu]=fv;f[ekr[i].u]=fv;f[ekr[i].v]=fv; 71 sum++; 72 } 73 if(tot==((n-1)<<1))break; 74 } 75 initdfs(1,0,233333333,0); 76 scanf("%d",&q); 77 for(int i=1;i<=q;i++){ 78 scanf("%d%d",&u,&v); 79 ans=233333333; 80 queue(u,v); 81 printf("%d\n",(ans==0)?-1:ans); 82 } 83 }