倍增求lca

这是一个在线算法——这是非常重要的一点,求一次的复杂度是logn

总复杂度为mlogn

个人过的,介绍几题1036 商务旅行 codevs

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
const int MAXN=30007;
using namespace std;
vector<int>to[MAXN];//记录到达的边
int n,deep[30007],a[30007],m,f[MAXN][20];

void init();
void dfs(int x,int dfn,int fa);
int lca(int x,int y);
void init_f();

int main()
{
    int x,y;
    scanf("%d",&n);
    init();
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        to[x].push_back(y);
        to[y].push_back(x);
    }
    dfs(1,1,-1);
    init_f();
    scanf("%d",&m);
    a[1]=0;
    for (int i=2;i<=m+1;i++)
        scanf("%d",&a[i]);
    int ans=0;
    for (int i=1;i<=m;i++)
    {
        x=lca(a[i],a[i+1]);
        ans+=(deep[a[i]]+deep[a[i+1]]-2*deep[x]);    
    }    
    cout<<ans-1<<endl;//应为点比经过的边多一
}
void init()
{
    for (int i=1;i<=n;i++)
    {
        to[i].clear();
    }
    memset(deep,0,sizeof(deep));
    memset(a,0,sizeof(a));
    memset(f,-1,sizeof(f));
}
void dfs(int x,int dfn,int fa)//dfn记录的是深度,fa是父亲
{
    deep[x]=dfn;
    f[x][0]=fa;
    int xx=to[x].size();
    for (int i=0;i<xx;i++)
    {
        if (to[x][i]!=fa) dfs(to[x][i],dfn+1,x);
    }
}
void init_f()//初始化f的值,以f[x][0]从dfs中求出来更新其他父亲。
{
    for (int j=1;(1<<j)<=n;j++)
        for (int i=1;i<=n;i++)
            if (f[i][j-1]!=-1) f[i][j]=f[f[i][j-1]][j-1];
}
int lca(int x,int y)
{
    int i;
    if (deep[x]<deep[y]) swap(x,y);//注意!这个必要的,否则下面影响
    for (i=0;(1<<i)<=deep[x];i++);
    i--;
    for (int j=i;j>=0;j--)
        if (deep[x]-(1<<j)>=deep[y]) x=f[x][j];//使其深度相同
    if (x==y) return x;
    for (int j=i;j>=0;j--)
    {
        if (f[x][j]!=-1&&f[x][j]!=f[y][j])
        {
            x=f[x][j];
            y=f[y][j];
        }
    }    
    return f[x][0];
}

另外一题难度差不多,只是边上多了一个权值,这时只是dfs稍加改进即可

http://codevs.cn/problem/2370/

也是codevs上的题,还可以。

题意也是一眼题

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
const int MAXN=50007;
using namespace std;
vector<int> to[MAXN],zhi[MAXN];
int n,m,deep[MAXN],f[MAXN][20],dis[MAXN];

void init();
void init_f();
int lca(int a,int b);
void dfs(int x,int dfn,int fa,int where);

int main()
{
    int x,y,z;
    init();
    scanf("%d",&n);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        to[x].push_back(y);
        to[y].push_back(x);
        zhi[x].push_back(z);
        zhi[y].push_back(z);
    }
    dfs(0,1,-1,-1);
    init_f();
    scanf("%d",&m);
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        z=lca(x,y);
        cout<<dis[x]+dis[y]-2*dis[z]<<endl;
    }
}
void init()
{
    for (int i=1;i<=MAXN;i++)
    {
        to[i].clear();
        zhi[i].clear();
    }
    memset(f,0,sizeof(f));
    memset(deep,0,sizeof(deep));
}
void dfs(int x,int dfn,int fa,int where)
{
    deep[x]=dfn;
    if (fa==-1) dis[x]=0;
    else dis[x]=dis[fa]+zhi[fa][where];
    int xx=to[x].size();
    for (int i=0;i<xx;i++)
    {
        if (to[x][i]==fa) continue;
        f[to[x][i]][0]=x;
        dfs(to[x][i],dfn+1,x,i);
    }
}
void init_f()
{
    for (int j=1;(1<<j)<=n;j++)
        for (int i=0;i<n;i++)
            if (f[i][j-1]!=-1) f[i][j]=f[f[i][j-1]][j-1];    
}
int lca(int a,int b)
{
    if (deep[a]<=deep[b]) swap(a,b);
    int i;
    for (i=0;(1<<i)<=deep[a];i++);
    i--;
    for (int j=i;j>=0;j--)
        if (deep[a]-(1<<j)>=deep[b]) a=f[a][j];
    if (a==b) return a;
    for (int j=i;j>=0;j--)
    {
        if (f[a][j]!=-1&&f[a][j]!=f[b][j])
        {
            a=f[a][j];
            b=f[b][j];
        }
    }    
    return f[a][0];
}

代码不长,倍增实现,在线算法,复杂度O(mlogn)

posted @ 2017-04-27 19:13  Kaiser-  阅读(138)  评论(0编辑  收藏  举报