zoukankan      html  css  js  c++  java
  • LCA——倍增求解

    LCA,即最近公共祖先,用于解决树上两点的最近公共祖先问题。

     ;

    lca(1,2)=3;(原谅我的绘画水平)

    LCA的求解有三种算法(我知道的)——tarjan,倍增,线段树(我只会两种),NOIp之前可以学了LCA,然后NOIp还是挂了,hhh

    以下为经典倍增代码

    /*
      f[i,j]表示第i个节点向上跳2^j步所到达的节点
      利用f[i,j]=f[f[i,j-1],j-1](向上跳j-1步后的节点再跳j-1步)递推求得
    */
    void lca(){
        for (int j=1;j<=20;j++)//保证j先i后
            for (int i=1;i<=n;i++)
                f[i][j]=f[f[i][j-1]][j-1];

    另附一道经典例(水)题

    1036 商务旅行

     

    【题目描述 Description】

    某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间。

    假设有N个城镇,首都编号为1,商人从首都出发,其他各城镇之间都有道路连接,任意两个城镇之间如果有直连道路,在他们之间行驶需要花费单位时间。该国公路网络发达,从首都出发能到达任意一个城镇,并且公路网络不会存在环。

    你的任务是帮助该商人计算一下他的最短旅行时间。

    【输入描述 Input Description】

    输入文件中的第一行有一个整数N,1<=n<=30 000,为城镇的数目。下面N-1行,每行由两个整数a 和b (1<=ab<=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

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    using namespace std;
    vector<int> edge[30010];
    int f[30010][22],u,v,n,m,h[30010]={0},fa[30010],ans;
    bool vis[30010];
    void add(int u,int v){
        edge[u].push_back(v);
    }
    
    void dfs(int now){
        for (int i=0;i<edge[now].size();i++){
            int mid=edge[now][i];
            if (vis[mid]) continue;
            vis[mid]=1;
            fa[mid]=now;
            h[mid]=h[now]+1;
            f[mid][0]=now;
            dfs(mid);
        }
    }
    
    void lca(){
        for (int j=1;j<=20;j++)
            for (int i=1;i<=n;i++)
                f[i][j]=f[f[i][j-1]][j-1];
    }
    
    int query(int u,int v){//这里wa的原因返回值时出错
        if (h[u]<h[v]) swap(u,v);
        if (h[u]!=h[v]){
        for (int i=20;i>=0;i--) {
        if (h[f[u][i]]>h[v]) 
        u=f[u][i];}
        u=f[u][0];
        }
        for (int i=20;i>=0;i--)
            if (f[u][i]!=f[v][i]){
                u=f[u][i];
                v=f[v][i];
            }
            if (u==v) return u;
            if (f[u][0]==v) return v;//用于特判,我也不知道对不对
            if (f[v][0]==u) return u;
            u=f[u][0]; v=f[v][0];//最后要再跳一步
            if (u==v) return u;
    }
    
    int main(){
        scanf("%d",&n);
        for (int i=0;i<n-1;i++){
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        vis[1]=1;
        dfs(1);
        fa[1]=1;
        lca(); 
        f[1][0]=1;
        scanf("%d",&m);
        u=1;
        for (int i=0;i<m;i++){
    
            scanf("%d",&v);
            int t=query(u,v);
            h[1]=0;
            ans+=h[u]+h[v]-2*h[t];
            u=v;
        }
        printf("%d",ans);
    }

    线段树的做法,下次填坑

  • 相关阅读:
    .net注册iis
    hdu 1081To The Max
    hdu 1312Red and Black
    hdu 1016Prime Ring Problem
    hdu 1159Common Subsequence
    hdu 1372Knight Moves
    hdu 1686Oulipo
    hdu 1241Oil Deposits
    hdu 1171Big Event in HDU
    hdu 4006The kth great number
  • 原文地址:https://www.cnblogs.com/wuminyan/p/5097291.html
Copyright © 2011-2022 走看看