codevs 1036 商务旅行 (倍增LCA)

时间:2022-03-10 16:56:55
/*
在我还不知道LCA之前 暴力跑的SPFA
70分 三个点TLE
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct node
{
int u;
int t;
int pre;
};
node a[*+];
queue<int>q;
int n,qq,x[],num,head[],f[],dis[],sum;
void add(int from,int to)
{
num++;
a[num].pre=head[from];
a[num].u=to;
a[num].t=;
head[from]=num;
}
void SPFA()
{
int i,j;
for(j=;j<=qq-;j++)
{
memset(dis,/,sizeof(dis));
memset(f,,sizeof(f));
int s=x[j];
int v=x[j+];
q.push(s);
f[s]=;
dis[s]=;
do
{
int k=q.front();
q.pop();
f[k]=;
for(i=head[k];i;i=a[i].pre)
{
if(dis[a[i].u]>dis[k]+a[i].t)
{
dis[a[i].u]=dis[k]+a[i].t;
if(f[a[i].u]==)
{
q.push(a[i].u);
f[a[i].u]=;
}
}
}
}while(!q.empty());
sum=sum+dis[v];
}
}
int main()
{
int i,o,u;
cin>>n;
for(i=;i<=n-;i++)
{
cin>>o>>u;
add(o,u);
add(u,o);
}
cin>>qq;
for(i=;i<=qq;i++)
cin>>x[i];
SPFA();
cout<<sum;
}
/*
倍增LCA
将旅行路线两两拆开 求相邻两点的距离
即求他们到lca的距离和
这里用倍增法实现求距离 fa[i][j]表示i节点往上2^j层的节点是什么
因为1 2 4 8 16...做和可以表示所有的整数 所以保证覆盖整张图
先Dfs建图 顺面几下每个节点的所在层 以及fa[i][0] 的值
然后DP预处理fa数组
然后就是求到lca的距离了
这里我们求出只需要求出lca是谁 那么两点之间的最小距离就是
deep[p1]+deep[p2]-2*deep[anc] deep是深度
剩下的就仅仅是找lca是谁了
对于一组a ,b 如果他们在同一层的话 我们只需要同时向上移动他俩 知道a==b */
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 30010
#define S 16
int n,m,num,head[maxn],fa[maxn][S+],deep[maxn],p1,p2,ans;
struct node
{
int u,v,pre;
}e[maxn*];
void Add(int from,int to)
{
num++;
e[num].u=from;
e[num].v=to;
e[num].pre=head[from];
head[from]=num;
}
void swap(int &a,int &b)
{
int t=a;a=b;b=t;
}
void init()
{
scanf("%d",&n);
int x,y;
for(int i=;i<=n-;i++)
{
scanf("%d%d",&x,&y);
Add(x,y);Add(y,x);
}
}
void get_fa()
{
for(int j=;j<=S;j++)
for(int i=;i<=n;i++)
fa[i][j]=fa[fa[i][j-]][j-];//数学方法展开就是fa[i][j]
}
void Dfs(int now,int from,int c)//得带每个点的所在层
{
fa[now][]=from;
deep[now]=c;
for(int i=head[now];i;i=e[i].pre)
if(e[i].v!=from)
Dfs(e[i].v,now,c+);
}
int get_same(int a,int t)//计算a往上t层是啥
{
for(int i=;i<=t;i++)
a=fa[a][];
return a;
}
int LCA(int a,int b)
{
if(deep[a]<deep[b])swap(a,b);//保证a深度大
a=get_same(a,deep[a]-deep[b]);//得到a向上deep[a]-deep[b]层是谁 即a的与b同层的祖先是谁
if(a==b)return a;//如果汇聚到一个点 就不用往上了
for(int i=S;i>=;i--)//这里有套路 从最大值开始循环 一开始 fa[a][i] fa[b][i]一定是相等的
//因为此时位于他们lca之上 然后往下 这里可以覆盖每一层
if(fa[a][i]!=fa[b][i])
{
a=fa[a][i];
b=fa[b][i];
}
return fa[a][];
}
int main()
{
init();
Dfs(,,);
get_fa();
scanf("%d%d",&m,&p1);
for(int i=;i<=m-;i++)
{
scanf("%d",&p2);
int anc=LCA(p1,p2);
ans+=deep[p1]+deep[p2]-*deep[anc];
p1=p2;
}
printf("%d\n",ans);
return ;
}