• Codevs 1036 商务旅行


    1036 商务旅行
    时间限制: 1 s
    空间限制: 128000 KB
    题目等级 : 钻石 Diamond
    传送门
    题目描述 Description
    某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间。
    假设有N个城镇,首都编号为1,商人从首都出发,其他各城镇之间都有道路连接,任意两个城镇之间如果有直连道路,在他们之间行驶需要花费单位时间。该国公路网络发达,从首都出发能到达任意一个城镇,并且公路网络不会存在环。
    你的任务是帮助该商人计算一下他的最短旅行时间。
    输入描述 Input Description
    输入文件中的第一行有一个整数N,1<=n<=30 000,为城镇的数目。下面N-1行,每行由两个整数a 和b (1<=a, b<=n; a<>b)组成,表示城镇a和城镇b有公路连接。在第N+1行为一个整数M,下面的M行,每行有该商人需要顺次经过的各城镇编号。
    输出描述 Output Description
    在输出文件中输出该商人旅行的最短时间。
    样例输入 Sample Input
    5
    1 2
    1 5
    3 5
    4 5
    4
    1
    3
    2
    5
    样例输出 Sample Output
    7
    数据范围及提示 Data Size & Hint
    分类标签 Tags
    最近公共祖先 图论 并查集 线段树 树结构

    /*
    在线LCA模板.
    fa[i][j]表示第i个点向上找2^j次所对应的点.
    所谓原理:2的幂可以组合成任意自然数(包括质数). 
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define MAXN 30001
    #define D 20
    using namespace std;
    struct data
    {
        int v;
        int next;
        int z;
    }
    e[MAXN*2];
    int tot,deep[MAXN],fa[MAXN][D+5],n,m,head[MAXN];
    void add_edge(int u,int v)
    {
        tot++;
        e[tot].v=v;
        e[tot].next=head[u];
        head[u]=tot;
    }
    void dfs(int now,int father,int d)//建树. 
    {
        fa[now][0]=father;
        deep[now]=d;
        for(int i=head[now];i;i=e[i].next)
        {
            if(father!=e[i].v)
            {
                dfs(e[i].v,now,d+1);
            }
    
        }
    }
    void get_father()//处理出每个节点的2^j. 
    {
        for(int j=1;j<=D;j++)
         for(int i=1;i<=n;i++)
           fa[i][j]=fa[fa[i][j-1]][j-1];
    }
    int get_same(int a,int b)//二进制使u,v在同一深度.
    {
        for(int i=0;i<=D;i++)
          if(b&(1<<i)) a=fa[a][i];
        return a;  
    }
    int lca(int u,int v)
    {
        if(deep[u]<deep[v]) swap(u,v);//让深的先搜便于计算. 
        u=get_same(u,deep[u]-deep[v]);//lca 
        if(u==v) return u;//u是v父亲时. 
        for(int i=D;i>=0;i--)//向上搜到lca的儿子. 
          if(fa[u][i]!=fa[v][i])
          {
            u=fa[u][i];
            v=fa[v][i];
          }
        return fa[u][0];//此时u为lca的子节点.
    }
    int main()
    {
        scanf("%d",&n);
        int x,y;
        for(int i=1;i<=n-1;i++)//▲一般是n-1条边.
        {
            scanf("%d%d",&x,&y);
            add_edge(x,y);
            add_edge(y,x);
        }
        dfs(1,1,0);
        get_father();
        int u,v;
        tot=0;
        scanf("%d%d",&m,&u);
        for(int i=1;i<m;i++)
        {
            scanf("%d",&v);
            int LCA=lca(u,v);
            tot+=(deep[u]+deep[v]-2*deep[LCA]);//数学方法. 
            u=v;
        }
        printf("%d",tot);
        return 0;
    }
    /*
    离线tarjan+并查集. 
    第一次打模板Wrong了.
    然后火急火燎找了半小时错误.
    关于邻接链表边的标号的初始化问题.
    若第一条边标号为0则需初始化head[1~n]=-1,
      因为搜索的时候必定会搜到head[u]==0 
      然后需判i!=-1
    若第一条边编号为1则只需判i!=0
      无需初始化.
    切记.  
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define MAXN 50001
    using namespace std;
    int n,m,head[MAXN],ans,fa[MAXN],lc[MAXN],tot,cut,deep[MAXN],head2[MAXN];
    bool b[MAXN],b2[MAXN];
    struct data
    {
        int v;
        int next;
    }
    e[MAXN<<1],s[MAXN<<1];
    void add_edge(int u,int v)
    {
        e[tot].v=v;
        e[tot].next=head[u];
        head[u]=tot++;
    }
    void add(int u,int v)
    {
        s[cut].v=v;
        s[cut].next=head2[u];
        head2[u]=cut++;
    }
    void dfs(int x,int father)
    {
        deep[x]=deep[father]+1;
        b[x]=true;
        for(int i=head[x];i!=-1;i=e[i].next)
        {
            if(!b[e[i].v])  dfs(e[i].v,x);
        }
    }
    int find(int x)
    {
        if(x==fa[x]) return fa[x];
        return fa[x]=find(fa[x]);
    }
    void lcatarjan(int u)
    {
        fa[u]=u;b[u]=true;
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            if(b[e[i].v]) continue;
            lcatarjan(e[i].v);
            fa[find(e[i].v)]=find(u);
            lc[find(u)]=u;
        }
        for(int i=head2[u];i;i=s[i].next)
        {
            if(b2[s[i].v])
            {
                ans+=deep[u]+deep[s[i].v]-2*deep[lc[find(s[i].v)]];
            }
        }
        b2[u]=true;
    }
    int main()
    {
        memset(head,-1,sizeof(head)); 
        memset(head2,-1,sizeof(head2)); 
        int x,y;
        scanf("%d",&n);
        for(int i=1;i<n;i++)
          {
            scanf("%d%d",&x,&y);
            add_edge(x,y);
            add_edge(y,x);
          }
        int u=1,v;
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&v);
            add(u,v);
            add(v,u);
            u=v; 
        }
    
        dfs(1,0);
        memset(b,0,sizeof(b));
        lcatarjan(1);
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    题解[LuoguP7419 「PMOI-2」参天大树]
    UVA11582 巨大的斐波那契数! Colossal Fibonacci Numbers!
    数学专题
    [计蒜客]dp
    [蓝桥杯每日一题]1.3 & 1.4
    【acm】2020icpc南京补题
    [acm]乐师师范学院校赛题解-2020
    西南交通大学峨眉校区第二届"INT"杯程序设计竞赛——决赛
    指针与结构体
    [acm] 动态规划——最长上升子序列
  • 原文地址:https://www.cnblogs.com/nancheng58/p/6070822.html
Copyright © 2020-2023  润新知