• 【NOIP2013/Codevs3287】货车运输-最小生成树(大)-树上倍增


    Problem 树上倍增

    题目大意

    给出一个图,给出若干个点对u,v,求u,v的一条路径,该路径上最小的边权值最大。

    Solution

    看到这个题第一反应是图论。。

    然而,任意路径最小的边权值最大,如果仔细思考的话就会知道,如果两个点相互连通,那么一定走的是最大生成树上的路径,而不会选择其他任何一条路径去走。

    这个是可以非常简单证明的,就不再详述。

    那么既然知道了这个,当然是先建一颗最大生成树啦!

    现在问题来了,Prim&Kruskal,选哪个?

    分析一下,prim复杂度$O(n^2)$,n为总点数。

    Kruskal复杂度$O(mlog_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
    ",(ans==0)?-1:ans);
    82     }
    83 }
  • 相关阅读:
    解决docker pull很慢的方法
    Linux 基础 Day1
    linux运维人员必须熟悉的运维工具汇总
    chrome 浏览器插件推荐
    只能运维主要职责
    Linux查看所有用户用命令
    ubuntu16.04 离线包安装docker
    2013-10
    ELK原理与介绍
    shell中各种括号的作用()、(())、[]、[[]]、{}
  • 原文地址:https://www.cnblogs.com/skylynf/p/7147762.html
Copyright © 2020-2023  润新知