2014.11.2 LCA离线Tarjan算法

来源:互联网 发布:淘宝买家掉包怎么办 编辑:程序博客网 时间:2024/04/28 13:41

首先介绍一下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

0 0