• codevs 1036 商务旅行 (倍增LCA)


    /*
    在我还不知道LCA之前 暴力跑的SPFA 
    70分 三个点TLE 
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    struct node
    {
        int u;
        int t;
        int pre;
    };
    node a[30010*2+100];
    queue<int>q;
    int n,qq,x[30010],num,head[30010],f[30010],dis[30010],sum;
    void add(int from,int to)
    {
        num++;
        a[num].pre=head[from];
        a[num].u=to;
        a[num].t=1;
        head[from]=num;
    }
    void SPFA()
    {
        int i,j;
        for(j=1;j<=qq-1;j++)
          {
              memset(dis,127/3,sizeof(dis));
            memset(f,0,sizeof(f));
              int s=x[j];
              int v=x[j+1];
              q.push(s);
              f[s]=1;
              dis[s]=0;
              do
              {
                  int k=q.front();
                  q.pop();
                  f[k]=0;
                  for(i=head[k];i;i=a[i].pre)
                    {
                        if(dis[a[i].u]>dis[k]+a[i].t)
                          {
                              dis[a[i].u]=dis[k]+a[i].t;
                              if(f[a[i].u]==0)
                                {
                                    q.push(a[i].u);
                                    f[a[i].u]=1;
                          }
                      }
                  }
              }while(!q.empty());
            sum=sum+dis[v];
          } 
    }
    int main()
    {
        int i,o,u;
        cin>>n;
        for(i=1;i<=n-1;i++)
          {
              cin>>o>>u;
              add(o,u);
              add(u,o);
          }
        cin>>qq;
        for(i=1;i<=qq;i++)
          cin>>x[i];
        SPFA();
        cout<<sum;
    }
    /*
    倍增LCA 
    将旅行路线两两拆开 求相邻两点的距离 
    即求他们到lca的距离和 
    这里用倍增法实现求距离 fa[i][j]表示i节点往上2^j层的节点是什么
    因为1 2 4 8 16...做和可以表示所有的整数 所以保证覆盖整张图
    先Dfs建图 顺面几下每个节点的所在层 以及fa[i][0] 的值
    然后DP预处理fa数组
    然后就是求到lca的距离了
    这里我们求出只需要求出lca是谁 那么两点之间的最小距离就是
    deep[p1]+deep[p2]-2*deep[anc] deep是深度
    剩下的就仅仅是找lca是谁了
    对于一组a ,b  如果他们在同一层的话 我们只需要同时向上移动他俩 知道a==b
     
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define maxn 30010
    #define S 16
    int n,m,num,head[maxn],fa[maxn][S+5],deep[maxn],p1,p2,ans;
    struct node
    {
        int u,v,pre;
    }e[maxn*2];
    void Add(int from,int to)
    {
        num++;
        e[num].u=from;
        e[num].v=to;
        e[num].pre=head[from];
        head[from]=num;
    }
    void swap(int &a,int &b)
    {
        int t=a;a=b;b=t;
    }
    void init()
    {
        scanf("%d",&n);
        int x,y;
        for(int i=1;i<=n-1;i++)
          {
              scanf("%d%d",&x,&y);
              Add(x,y);Add(y,x);
          }
    }
    void get_fa()
    {
        for(int j=1;j<=S;j++)
          for(int i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];//数学方法展开就是fa[i][j] 
    }
    void Dfs(int now,int from,int c)//得带每个点的所在层 
    {
        fa[now][0]=from;
        deep[now]=c;
        for(int i=head[now];i;i=e[i].pre)
          if(e[i].v!=from)
            Dfs(e[i].v,now,c+1);
    }
    int get_same(int a,int t)//计算a往上t层是啥 
    {
        for(int i=1;i<=t;i++)
          a=fa[a][0];
        return a;
    }
    int LCA(int a,int b)
    {
        if(deep[a]<deep[b])swap(a,b);//保证a深度大 
        a=get_same(a,deep[a]-deep[b]);//得到a向上deep[a]-deep[b]层是谁 即a的与b同层的祖先是谁 
        if(a==b)return a;//如果汇聚到一个点 就不用往上了 
        for(int i=S;i>=0;i--)//这里有套路 从最大值开始循环 一开始 fa[a][i]  fa[b][i]一定是相等的
                             //因为此时位于他们lca之上 然后往下 这里可以覆盖每一层 
          if(fa[a][i]!=fa[b][i])
            {
              a=fa[a][i];
              b=fa[b][i];
            }
        return fa[a][0];
    }
    int main()
    {
        init();
        Dfs(1,1,0);
        get_fa();
        scanf("%d%d",&m,&p1);
        for(int i=1;i<=m-1;i++)
          {
              scanf("%d",&p2);
              int anc=LCA(p1,p2);
              ans+=deep[p1]+deep[p2]-2*deep[anc];
              p1=p2;
          }
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    快速幂模板
    部分有关素数的题
    POJ 3624 Charm Bracelet (01背包)
    51Nod 1085 背包问题 (01背包)
    POJ 1789 Truck History (Kruskal 最小生成树)
    HDU 1996 汉诺塔VI
    HDU 2511 汉诺塔X
    HDU 2175 汉诺塔IX (递推)
    HDU 2077 汉诺塔IV (递推)
    HDU 2064 汉诺塔III (递推)
  • 原文地址:https://www.cnblogs.com/yanlifneg/p/5542449.html
Copyright © 2020-2023  润新知