您的位置:首页 > 其它

【LCA 倍增法】【codevs 1036 商务旅行】

2016-04-25 17:44 363 查看
题目

题目描述 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

在输出文件中输出该商人旅行的最短时间。

#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;
#define maxn 30010
#define exc(a,b) { a = a^b;b = a^b;a = a^b; }
#define cint const int&
int p[maxn][16];
struct edge{
int to;
edge* next;
};

edge* head[maxn];
int dep[maxn];
void dfs(cint u,cint fa){
dep[u] = dep[fa]+1;
p[u][0] = fa;
for(int i = 1;i <= 15;++i) p[u][i] = p[p[u][i-1]][i-1];
for(edge* i = head[u];i!=NULL;i=i->next){
if(i->to != fa) dfs(i->to,u);
}
}

void adde(cint a,cint b){
edge* n = new edge;
n->to = b;n->next = head[a];
head[a] = n;
}

int lca(int a,int b){
if(dep[a]>dep[b]) exc(a,b);
for(int i = 15;i>=0;--i)if(dep[a]<=dep[b] - (1<<i)) b = p[b][i];

if(a==b) return a;

for(int i = 15;i >= 0;--i){
if(p[a][i]==p[b][i]) continue;
a = p[a][i];b = p[b][i];
}
return p[a][0];
}

int main(){
int n,m;
scanf("%d",&n);
int a,b;
for(int i = 1;i < n;++i){
scanf("%d%d",&a,&b);
adde(a,b);adde(b,a);
}
dfs(1,0);
int ans(0);
scanf("%d",&m);
scanf("%d",&a);
for(int i = 1;i < m;++i){
scanf("%d",&b);
ans += dep[a] + dep[b] - 2 * dep[lca(a,b)];
a = b;
}
cout << ans;
return 0;
}

贴另一优美的代码。2333.

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
#define maxn 30100
using namespace std;
int n;
struct edge{
int to,next;
}e[4*maxn];
int k=1,head[maxn];
int p[maxn][25];
int d[maxn];
void adde(int a,int b)
{
e[k].to=b;
e[k].next=head[a];
head[a]=k++;
}
//首先预处理出所有的p[i][j],并计算每个点的深度d[i]
void dfs(int u,int fa)///从根结点u开始dfs,u的父亲是fa
{
d[u]=d[fa]+1;//u的深度为它父亲的深度+1
p[u][0]=fa;//u向上走2^0步到达的结点是其父亲
for(int i=1;i<=20;i++)p[u][i]=p[p[u][i-1]][i-1];//预处理p时,保证能从u走到p[u][i]
for(int i=head[u];i!=-1;i=e[i].next)///对于u的每个儿子
{
int v=e[i].to;
if(v!=fa)dfs(v,u);///递归处理以v为根结点的子树
}
}
int m;
int ans=0;
int lca(int a,int b)
{
if(d[a]>d[b])swap(a,b);///保证a点在b点上面
for(int j=20;j>=0;--j) if(d[a] <= d[b] - (1<<j))b = p[b][j];
////将b点向上移动到和a的同一深度
if(a==b)return a;//如果a和b相遇
for(int i=20;i>=0;--i)//a,b同时向上移动
{
if(p[a][i]==p[b][i]) continue;//如果a,b的2^j祖先相同,则不移动
a = p[a][i];b = p[b][i];//否则同时移动到2^j处
}
return p[a][0];//返回最后a的父亲结点
}
/******Program Begin*********/
int main()
{
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(int i=1;i<=n;i++)d[i]=1;
int a,b;
for(int i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
adde(a,b);
adde(b,a);
}
dfs(1,0);
scanf("%d",&m);
scanf("%d",&a);
ans=0;
for(int i=1;i<m;i++)
{
scanf("%d",&b);
ans+=d[a]+d[b]-2*d[lca(a,b)];
a=b;
}
printf("%d",ans);
return 0;
}
/*******Program End**********/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: