2014.11.2 LCA离线Tarjan算法

首先介绍一下LCA

LCA(Least Common Ancestor)最近公共祖先,顾名思义,是指树上两点到根节点路径最早出现的公共节点,目前主要有两种算法

一、在线的倍增算法(可在算出lca同时计算最小(大)权值,路径长度)

二、离线的Tarjan(时间复杂度较小,但只能求出lca)

------------------------------------------------------------------------------------------------------------------------------------

两相抉择,我选择了Tarjan算法。

tarjan算法简单来说就是DFS+并查集,每次dfs到一个节点,先dfs它的儿子节点,这样就解决了它儿子的lca问题(并查集)。不断的DFS一棵树以后也就解决了所有询问,先贴STANDARD CODE:

intf[maxn],fs[maxn];//并查集父节点 父节点个数
boolvit[maxn];
intanc[maxn];//祖先
vector<int> son[maxn];//保存树
vector<int> qes[maxn];//保存查询
typedefvector<int>::iterator IT;
 
intFind(intx)
{
    if(f[x]==x)returnx;
    elsereturnf[x]=Find(f[x]);
}
voidUnion(intx,inty)//启发式合并
{
    x=Find(x);y=Find(y);
    if(x==y)return;
    if(fs[x]<=fs[y]) f[x]=y,fs[y]+=fs[x];
    elsef[y]=x,fs[x]+=fs[y];
}
 
voidlca(intu)
{
    anc[u]=u;
    for(IT v=son[u].begin();v!=son[u].end();++v)//穷举儿子节点
    {
        lca(*v);
        Union(u,*v);
        anc[Find(u)]=u;//解决儿子的lca
    }
    vit[u]=true;
    for(IT v=qes[u].begin();v!=qes[u].end();++v)
    {
        if(vit[*v])
            printf("LCA(%d,%d):%d\n",u,*v,anc[Find(*v)]);
    }
}//(来自scturtle)
------------------------------------------------------------------------------------------------------------------------------------------------------
例题,codevs2370 小机房的树
小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上。有一天,他们想爬到一个节点上去搞基,但是作为两只虫子,他们不想花费太多精力。已知从某个节点爬到其父亲节点要花费 c 的能量(从父亲节点爬到此节点也相同),他们想找出一条花费精力最短的路,以使得搞基的时候精力旺盛,他们找到你要你设计一个程序来找到这条路,要求你告诉他们最少需要花费多少精力
codevs 1036 商务旅行

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

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

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


裸的lca不解释,不过要把处理过的询问加到lca上,在find函数中对其到其父亲的距离更新即可

code 2370
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
vector<int> son[50001][2];
vector<int> qes[75001][2];
vector<int> anc[50001][2];
int n,m,q;
bool f[50001]={false};
int father[50001][2];
int ans[75001]={0};
int find(int x)
{
int j;
if (father[x][0]!=x)
 {
   j=find(father[x][0]);
   father[x][1]+=father[father[x][0]][1];
   father[x][0]=j;
      }
    return father[x][0];  
}
void LCA(int x,int y)
{
int i,root;
for (i=0;i<son[x][0].size();++i)
 if (son[x][0][i]!=y)
   {
     LCA(son[x][0][i],x);
     father[son[x][0][i]][0]=x;
     father[son[x][0][i]][1]=son[x][1][i];
   }    
f[x]=true;
for (i=0;i<qes[x][0].size();++i)
 if (f[qes[x][0][i]])
   {
     root=find(qes[x][0][i]);
     anc[root][0].push_back(x);
     anc[root][1].push_back(qes[x][1][i]);
     ans[qes[x][1][i]]=father[qes[x][0][i]][1];
   }
for (i=0;i<anc[x][0].size();++i)
 {
   find(anc[x][0][i]);
   ans[anc[x][1][i]]+=father[anc[x][0][i]][1];
 }
}
int main()
{
int u,v,w,i,j;
scanf("%d",&n);
memset(father,0,sizeof(father));
for (i=0;i<=n-1;++i)
 {father[i][0]=i;
 anc[i][0].clear();
 anc[i][1].clear();
 son[i][0].clear();
 son[i][1].clear();
 qes[i][0].clear();
 qes[i][1].clear();}
for (i=1;i<=n-1;++i)
 {
   scanf("%d%d%d",&u,&v,&w);
son[u][0].push_back(v);
son[u][1].push_back(w);
son[v][0].push_back(u);
son[v][1].push_back(w);    
 } 
scanf("%d",&q);
for (i=1;i<=q;++i)
 {
   scanf("%d%d",&u,&v);
   if (u!=v)
     {
       qes[u][0].push_back(v);
       qes[u][1].push_back(i);
       qes[v][0].push_back(u);
       qes[v][1].push_back(i);
     }
 }
LCA(1,-1);
for(i=1;i<=q;++i)
 cout<<ans[i]<<endl;
}
code 1036 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
vector<int> son[30001];
vector<int> qes[100001];
vector<int> anc[30001][2];
int n,m;
bool f[30001]={false};
int father[30001][2];
long long ans;
int find(int x)
{
int j;
if (father[x][0]!=x)
 {
   j=find(father[x][0]);
   father[x][1]+=father[father[x][0]][1];
   father[x][0]=j;
      }
    return father[x][0];  
}
void LCA(int x,int y)
{
int i,root;
for (i=0;i<son[x].size();++i)
 if (son[x][i]!=y)
   {
     LCA(son[x][i],x);
     father[son[x][i]][0]=x;
     father[son[x][i]][1]=1;
   }
f[x]=true;
for (i=0;i<qes[x].size();++i)
 if (f[qes[x][i]])
   {
     root=find(qes[x][i]);
     anc[root][0].push_back(x);
     anc[root][1].push_back(qes[x][i]);
   }
for (i=0;i<anc[x][0].size();++i)
 {
   find(anc[x][0][i]); find(anc[x][1][i]);
   ans+=father[anc[x][0][i]][1]+father[anc[x][1][i]][1];
 }
}
int main()
{
int u,v,i,j;
scanf("%d",&n);
memset(father,0,sizeof(father));
for (i=1;i<=n;++i)
 {father[i][0]=i;anc[i][1].clear();anc[i][1].clear();son[i].clear();qes[i].clear();}
for (i=1;i<=n-1;++i)
 {
   scanf("%d%d",&u,&v);
son[u].push_back(v);
son[v].push_back(u);    
 } 
scanf("%d",&m);
u=1;
for (i=1;i<=m;++i)
 {
   scanf("%d",&v);
   if (u!=v)
     {
       qes[u].push_back(v);
       qes[v].push_back(u);
     }
   u=v;
 }
ans=0;
LCA(1,0);
cout<<ans<<endl;
}
------------------------------------------------------------------------------------------------------------------------------------------------------
货车运输 noip 2013 day1T3
题面不解释,首先构造最大生成树,再求出最大生成树上两点路径之间权值最小
此题倍增可过,但本蒟蒻的渣Tarjan在codevs60,Vijos AC,COGS AC)
code:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector> 
#include<algorithm>
using namespace std;
struct hp{
int u,v,w;
}a[55001];
int father[15001][2],ans[35001]={0};
vector<int> qes[35001][3];
vector<int> son[35001][3];
vector<int> anc[15001][3];
bool f[15001]={false};
int cmp(const hp &a,const hp &b)
{
if (a.w>b.w) return 1;
else return 0;
}
int find(int x)
{
if (father[x][0]==x)
 return x;
return find(father[x][0]);
}
int findx(int x)
{
int j;
if (father[x][0]!=x)
 {
   j=findx(father[x][0]);
father[x][1]=min(father[x][1],father[father[x][0]][1]);
father[x][0]=j;
 }
return father[x][0];
}
void LCA(int x,int y)
{
int i,root; 
for (i=0;i<son[x][1].size();++i)
 if (son[x][1][i]!=y)
   {
     LCA(son[x][1][i],x);
     father[son[x][1][i]][0]=x;
     father[son[x][1][i]][1]=son[x][2][i];
        }
f[x]=true;
for (i=0;i<qes[x][1].size();++i)
 if (f[qes[x][1][i]])
   {
     root=findx(qes[x][1][i]);
     anc[root][0].push_back(qes[x][2][i]);
     anc[root][1].push_back(x);
     anc[root][2].push_back(qes[x][1][i]);
   }
for (i=0;i<anc[x][0].size();++i)
 {
   findx(anc[x][1][i]); findx(anc[x][2][i]);
   ans[anc[x][0][i]]=min(father[anc[x][1][i]][1],father[anc[x][2][i]][1]);
 }
}
int main()
{
int u,v,k,r1,r2,i,j,n,m,q,root;
//freopen("truck.in","r",stdin);
//freopen("truck.out","w",stdout);
scanf("%d%d",&n,&m);
memset(father,127,sizeof(father));
for (i=1;i<=n;++i)
 {son[i][1].clear(); son[i][2].clear(); qes[i][1].clear(); qes[i][2].clear(); father[i][0]=i;}
for (i=1;i<=m;++i)
 scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
sort(a+1,a+m+1,cmp);
k=0;
for (i=1;i<=m;++i)
 {
   r1=find(a[i].u);
   r2=find(a[i].v);
   if (r1!=r2)
     {
       father[r1][0]=r2;
       u=a[i].u;
       v=a[i].v;
       son[u][1].push_back(v);
       son[u][2].push_back(a[i].w);
       son[v][1].push_back(u);
son[v][2].push_back(a[i].w);
k++;
     }
   if (k==n-1)
     break;
 }
scanf("%d",&q);
for (i=1;i<=q;++i)
 {
   scanf("%d%d",&u,&v);
   if (find(u)!=find(v))
     ans[i]=-1;
   else
     {
       qes[u][1].push_back(v);
       qes[u][2].push_back(i);
       qes[v][1].push_back(u);
       qes[v][2].push_back(i);
     }
 }
for (i=1;i<=n;++i)
 father[i][0]=i;
for (i=1;i<=n;++i)
 if (!f[i])
   LCA(i,0);
for (i=1;i<=q;++i)
 printf("%d\n",ans[i]);  
//fclose(stdin);
//fclose(stdout);
}
<span style="font-family:SimSun;"> </span>
------------------------------------------------------------------------------------------------------------------------------------------------------
感谢同机房神犇TA;
lcomyn
2014.11.2

你可能感兴趣的:(2014.11.2 LCA离线Tarjan算法)