您的位置:首页 > 其它

Codevs 1036 商务旅行

2016-07-09 06:10 344 查看
1036 商务旅行

时间限制: 1 s

空间限制: 128000 KB

题目等级 : 钻石 Diamond

传送门

题目描述 Description

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

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

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

输入描述 Input Description

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

数据范围及提示 Data Size & Hint

分类标签 Tags

最近公共祖先 图论 并查集 线段树 树结构

/*
在线LCA模板.
fa[i][j]表示第i个点向上找2^j次所对应的点.
所谓原理:2的幂可以组合成任意自然数(包括质数).
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define MAXN 30001
#define D 20
using namespace std;
struct data
{
int v;
int next;
int z;
}
e[MAXN*2];
int tot,deep[MAXN],fa[MAXN][D+5],n,m,head[MAXN];
void add_edge(int u,int v)
{
tot++;
e[tot].v=v;
e[tot].next=head[u];
head[u]=tot;
}
void dfs(int now,int father,int d)//建树.
{
fa[now][0]=father;
deep[now]=d;
for(int i=head[now];i;i=e[i].next)
{
if(father!=e[i].v)
{
dfs(e[i].v,now,d+1);
}

}
}
void get_father()//处理出每个节点的2^j.
{
for(int j=1;j<=D;j++)
for(int i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1];
}
int get_same(int a,int b)//二进制使u,v在同一深度.
{
for(int i=0;i<=D;i++)
if(b&(1<<i)) a=fa[a][i];
return a;
}
int lca(int u,int v)
{
if(deep[u]<deep[v]) swap(u,v);//让深的先搜便于计算.
u=get_same(u,deep[u]-deep[v]);//lca
if(u==v) return u;//u是v父亲时.
for(int i=D;i>=0;i--)//向上搜到lca的儿子.
if(fa[u][i]!=fa[v][i])
{
u=fa[u][i];
v=fa[v][i];
}
return fa[u][0];//此时u为lca的子节点.
}
int main()
{
scanf("%d",&n);
int x,y;
for(int i=1;i<=n-1;i++)//▲一般是n-1条边.
{
scanf("%d%d",&x,&y);
add_edge(x,y);
add_edge(y,x);
}
dfs(1,1,0);
get_father();
int u,v;
tot=0;
scanf("%d%d",&m,&u);
for(int i=1;i<m;i++)
{
scanf("%d",&v);
int LCA=lca(u,v);
tot+=(deep[u]+deep[v]-2*deep[LCA]);//数学方法.
u=v;
}
printf("%d",tot);
return 0;
}


/*
离线tarjan+并查集.
第一次打模板Wrong了.
然后火急火燎找了半小时错误.
关于邻接链表边的标号的初始化问题.
若第一条边标号为0则需初始化head[1~n]=-1,
因为搜索的时候必定会搜到head[u]==0
然后需判i!=-1
若第一条边编号为1则只需判i!=0
无需初始化.
切记.
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define MAXN 50001
using namespace std;
int n,m,head[MAXN],ans,fa[MAXN],lc[MAXN],tot,cut,deep[MAXN],head2[MAXN];
bool b[MAXN],b2[MAXN];
struct data
{
int v;
int next;
}
e[MAXN<<1],s[MAXN<<1];
void add_edge(int u,int v)
{
e[tot].v=v;
e[tot].next=head[u];
head[u]=tot++;
}
void add(int u,int v)
{
s[cut].v=v;
s[cut].next=head2[u];
head2[u]=cut++;
}
void dfs(int x,int father)
{
deep[x]=deep[father]+1;
b[x]=true;
for(int i=head[x];i!=-1;i=e[i].next)
{
if(!b[e[i].v])  dfs(e[i].v,x);
}
}
int find(int x)
{
if(x==fa[x]) return fa[x];
return fa[x]=find(fa[x]);
}
void lcatarjan(int u)
{
fa[u]=u;b[u]=true;
for(int i=head[u];i!=-1;i=e[i].next)
{
if(b[e[i].v]) continue;
lcatarjan(e[i].v);
fa[find(e[i].v)]=find(u);
lc[find(u)]=u;
}
for(int i=head2[u];i;i=s[i].next)
{
if(b2[s[i].v])
{
ans+=deep[u]+deep[s[i].v]-2*deep[lc[find(s[i].v)]];
}
}
b2[u]=true;
}
int main()
{
memset(head,-1,sizeof(head));
memset(head2,-1,sizeof(head2));
int x,y;
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add_edge(x,y);
add_edge(y,x);
}
int u=1,v;
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d",&v);
add(u,v);
add(v,u);
u=v;
}

dfs(1,0);
memset(b,0,sizeof(b));
lcatarjan(1);
printf("%d",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: