发现两道重题233
一道很裸很裸的倍增lca题。
一开始还以为边权不同,被震惊到了,想了半天无果,正打算搜题解,然后又看了眼题,发现。。。
其实对于这三个节点,其集合点必然在三个lca中的一个,然后这一个一定是深度最深的那一个。原因是答案中从节点爬到lca这一部分值是不变的,变的是从lca爬到集合点的这一部分dist。若选择深度较浅的,必然意味着有两个节点要从深lca节点爬至浅lca节点,这里是二倍的dist;而若选择深lca节点,只需要一个节点从浅lca节点到深的,代价是dist。综上,选择较深的lca节点。
然后就是一道很裸的倍增lca了。。。
一开始想用tarjan,后来发现还要算代价,这样lca求起来就很混乱,于是不如用好写一些的倍增吧。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=500005;
struct edge
{
int to,next;
}e[maxn<<1];
int n,m;
int head[maxn];
int depth[maxn];
int ff[maxn][21];
inline void insert(int a,int b)
{
static int cnt=0;
e[++cnt].to=b;e[cnt].next=head[a];head[a]=cnt;
e[++cnt].to=a;e[cnt].next=head[b];head[b]=cnt;
}
bool cmp(int x,int y)
{
return depth[x]>depth[y];
}
void dfs(int x,int fa)
{
depth[x]=depth[fa]+1;
ff[x][0]=fa;
for(int i=head[x];i;i=e[i].next)
{
if(e[i].to==fa)continue;
dfs(e[i].to,x);
}
}
void st_chart()
{
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++)
ff[j][i]=ff[ff[j][i-1]][i-1];
}
int lca(int x,int y)
{
if(depth[x]<depth[y])swap(x,y);
int delta=depth[x]-depth[y];
int pos=0;
while(delta)
{
if(delta&1)x=ff[x][pos];
pos++;
delta>>=1;
}
if(x==y)return x;
for(int i=20;i>=0;i--)
if(ff[x][i]!=ff[y][i])
x=ff[x][i],y=ff[y][i];
return ff[x][0];
}
inline int dist(int x,int y)
{
return depth[x]+depth[y]-2*depth[lca(x,y)];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
insert(a,b);
}
dfs(1,0);
st_chart();
int s[3];
for(int i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
s[0]=lca(a,b);
s[1]=lca(a,c);
s[2]=lca(b,c);
sort(s,s+3,cmp);
printf("%d %d\n",s[0],dist(s[0],a)+dist(s[0],b)+dist(s[0],c));
}
return 0;
}