您的位置:首页 > 其它

hdu 2586 How far away ?(在线LCA+离线Tarjan)

2017-08-08 09:06 417 查看

How far away ?

题目链接:How far away ?

题意:一个村子里有n个房子,这n个房子用n-1条路连接起来,接下了有m次询问,每次询问两个房子a,b之间的最短距离是多少。

思路:LCA模板题

代码(离线Tarjan):

#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;

const int maxn=4e4+210;
vector<int>v[maxn],query[maxn],num[maxn],w[maxn];
int pre[maxn],dis[maxn],ans[maxn];
bool vis[maxn];
int n,m;

void Init()
{
for(int i=1;i<=n;++i)
{
v[i].clear();
query[i].clear();
num[i].clear();
w[i].clear();
pre[i]=i;
dis[i]=0;
vis[i]=false;
}
}

int Find(int x)
{
if(x!=pre[x])
pre[x]=Find(pre[x]);
return pre[x];
}

void join(int x,int y)
{
int fx=Find(x),fy=Find(y);
if(fx!=fy)
pre[fy]=fx;
}

void Tarjan(int rt,int val)
{
vis[rt]=true;
dis[rt]=val;
for(int i=0;i<v[rt].size();++i)
{
int tmp=v[rt][i];
if(!vis[tmp])
{
Tarjan(tmp,val+w[rt][i]);
join(rt,tmp);
}
}
for(int i=0;i<query[rt].size();++i)
{
int tmp=query[rt][i];
if(vis[tmp])
ans[num[rt][i]]=dis[rt]+dis[tmp]-2*dis[Find(tmp)];
}
}

int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
Init();
int x,y,val;
for(int i=1;i<n;++i)
{
scanf("%d%d%d",&x,&y,&val);
v[x].push_back(y);
w[x].push_back(val);
v[y].push_back(x);
w[y].push_back(val);
}
for(int i=1;i<=m;++i)
{
scanf("%d%d",&x,&y);
query[x].push_back(y);
num[x].push_back(i);
query[y].push_back(x);
num[y].push_back(i);
}
Tarjan(1,0);
for(int i=1;i<=m;++i)
printf("%d\n",ans[i]);
}
return 0;
}


这个很简单,时间复杂度O(N+Q),ve
109c2
ctor可以用邻接表优化一下

大神博客详解:离线Tarjan

代码(在线RMQ+DFS序):

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;

const int maxn=4e4+10;
struct edge
{
int v,w,next,flag;
} E[maxn<<1];
int first[maxn],val[maxn],dep[maxn],a[maxn<<1],b[maxn],f[maxn<<1][20],lca[maxn<<1][20];
int top,len,n,m;

void dfs(int x,int step,int va)
{
dep[x]=step;
a[++top]=x,val[x]=va;
for(int i=first[x]; ~i; i=E[i].next)
{
if(E[i].flag)
continue;
E[i].flag=E[i^1].flag=1;
int v=E[i].v,w=E[i].w;
dfs(v,step+1,va+w);
a[++top]=x;
}
}

void init()
{
for(int i=1; i<=top; ++i)
{
f[i][0]=dep[a[i]];
lca[i][0]=a[i];
}
int s=(int)log2(top*1.0);
for(int j=1; j<=s; ++j)
{
int k=top-(1<<j)+1;
for(int i=1; i<=k; ++i)
{
int x=i+(1<<(j-1));
if(f[i][j-1]<=f[x][j-1])
{
f[i][j]=f[i][j-1];
lca[i][j]=lca[i][j-1];
}
else
{
f[i][j]=f[x][j-1];
lca[i][j]=lca[x][j-1];
}
}
}
}

void add_edge(int u,int v,int w)
{
E[len].v=v,E[len].w=w,E[len].flag=0,E[len].next=first[u],first[u]=len++;
}

int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(b,0,sizeof(b));
memset(first,-1,sizeof(first));
scanf("%d%d",&n,&m);
len=0,top=0;
int u,v,w;
for(int i=1; i<n; ++i)
{
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w);
add_edge(v,u,w);
}
dfs(1,1,0);
for(int i=1; i<=top; ++i)
if(b[a[i]]==0)
b[a[i]]=i;
init();
while(m--)
{
scanf("%d%d",&u,&v);
u=b[u],v=b[v];
if(u>v)
swap(u,v);
int j=(int)log2((v-u)*1.0);
int i=v-(1<<j)+1;
int fa=f[u][j]<f[i][j]?lca[u][j]:lca[i][j];
printf("%d\n",val[a[u]]+val[a[v]]-2*val[fa]);
}
}
return 0;
}


在线算法把一棵树转化为了一维数组,a[]表示树上的节点在数组中的位置,b[]表示树上的节点在数组中第一次出现的位置

则任意两个节点i和j,在[abi,abj]中深度最小的那个节点就是他们的最近公共祖先

预处理的时间复杂度O(nlog2n),查询O(1)

大神博客详解:求LCA最近公共祖先的在线ST算法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: