题意还是很有病的,说了半天后边的方向都没用的,意思就是跟样例一样求两点间的距离,裸的LCA
两个点x,y的距离=dis[x]+dis[y]-2*dis[LCA(x,y)];
三个点x,y,z的距离=dis[x]+dis[y]+dis[z]-dis(lca(x,y))-dis(lca(y,z))-dis(lca(x,z));
倍增的思想是感觉跟RMQ很像,就是首先建数存路径与各节点深度,首先来说最暴力的方法就是将两点中深度大的点跳到与深度小的点一样深度,然后两个点一起往上跳一层一层的跳,最后总能跳到同一个位置。like this,但是显然复杂度大的不谈。
if(dep[u]<dep[v]) swap(u,v);
int x=(dep[u]-dep[v]);
while(x--)
u=f[u];
while(u!=v)
{
u=f[u];
v=f[v];
}
所以说倍增的方法就是通过一个二维的f[u][i]数组来表示u节点往上跳2^i方个节点。
那么比如说一个深度为10的u点和一个深度为5的v点,先将u点跳到v点的深度,就需要u先往上跳5个,转化成二进制不就是101.
那么u先往上跳一格f变成 f[u][0],然后再想上跳4格就是2的2次方 f[f[u][0]] [2],然后就跳到一个层了。
然后两个一起向上跳也是一样的,不再是一个一个的向上跳,而是尽量以2的n次方向上跳,也就是说深度差为int范围内只需跳不超过32次。所以就大大简化了复杂度,并且是在线 的。
那么怎么求f数组呢,在理解就是f[u][i]当i>0时,f[u][i]=f[f[u][i-1]] [i-1]类似于折半的感觉就能把f数组提前处理出来。
代码
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<set>
#include<stack>
#include<vector>
#include<map>
#include<queue>
#define myself i,l,r
#define lson i<<1
#define rson i<<1|1
#define Lson i<<1,l,mid
#define Rson i<<1|1,mid+1,r
#define half (l+r)/2
#define inff 0x3f3f3f3f
#define lowbit(x) x&(-x)
#define PI 3.14159265358979323846
#define me(a,b) memset(a,b,sizeof(a))
#define min4(a,b,c,d) min(min(a,b),min(c,d))
#define min3(x,y,z) min(min(x,y),min(y,z))
const int dir[4][2]= {0,-1,-1,0,0,1,1,0};
typedef long long ll;
const ll inFF=9223372036854775807;
typedef unsigned long long ull;
using namespace std;
const int maxn=4e4+5;
int f[maxn][20],d[maxn],head[maxn],dis[maxn];//d表示深度,dis表示到root距离,f表示父节点关系
int n,m,sign;
struct node
{
int to,p,val;
}edge[maxn<<1];
void init()
{
sign=0;
for(int i=1;i<=n;i++)
head[i]=-1;
}
void add(int u,int v,int val)
{
edge[sign]=node{v,head[u],val};
head[u]=sign++;
}
void dfs(int u)
{
for(int i=head[u];~i;i=edge[i].p)
{
int v=edge[i].to;
if(v==f[u][0]) continue;
d[v]=d[u]+1;
dis[v]=edge[i].val+dis[u];
f[v][0]=u;//f[v][0]就是v的father就是u
dfs(v);
}
}
void getf() //得到数组也是上述的那个理解
{
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
}
int lca(int a,int b)
{
if(d[a]<d[b]) swap(a,b);//把a变成那个深度大的
int x=d[a]-d[b];//距离差
for(int i=0;(1<<i)<=x;i++)
if((1<<i)&x) a=f[a][i];//把a跳到跟b一样的深度
if(a!=b)
{
for(int i=(int)log2(n);i>=0;i--)//这个要从大的步子往小步子跳
if(f[a][i]!=f[b][i]) a=f[a][i],b=f[b][i];
a=f[a][0];
}
return a;//返回lca
}
int main()
{
char ch;
int x,y,z,k;
while(cin>>n>>m)
{
init();
for(int i=1;i<=m;i++) scanf("%d %d %d %c",&x,&y,&z,&ch),add(x,y,z),add(y,x,z);
f[1][0]=1,dis[1]=0,d[1]=1;
dfs(1);
getf();
cin>>k;
for(int i=1;i<=k;i++){
scanf("%d %d",&x,&y);
cout<<dis[x]+dis[y]-2*dis[lca(x,y)]<<endl;
}
}
return 0;
}