聚会「AHOI 2008」

题意

有一颗树,多次询问:给出三个节点,求节点(k)使距离之和最小,且求距离。

思路

(lca)裸题。

这里可以证明一个性质:两两(lca)会得出三个节点,其中至少有两个重合。

证明:

显然有三种分布情况:

  • 三个节点都在同一颗子树上,这时公共(lca)显然为子树根。

  • 两个节点在同一颗子树上。假设这两个点((a,b))的(lca)(x),而另一个节点为(y),那么(lca(a,y)=lca(b,y)=lca(x,y))。显然有两个重合。

  • 三个节点都在不同子树上,同第一种情况。

(Q.E.D)

有了上面的性质,我们在计算的时候可以稍微减少计算量,

(ans=dep[a]+dep[b]+dep[c]-dep[lca(a,b)]+dep[lca(b,c)]+dep[lca(c,a)])

但是这样还是过不了,因为(OJ)卡倍增(lca),所以必须使用树剖求(lca)


代码

(倍增90分)

#include <bits/stdc++.h>

using namespace std;

namespace StandardIO {

	template<typename T> inline void read (T &x) {
		x=0;T f=1;char c=getchar();
		for (; c<'0'||c>'9'; c=getchar()) if (c=='-') f=-1;
		for (; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
		x*=f;
	}
	template<typename T> inline void write (T x) {
		if (x<0) putchar('-'),x=-x;
		if (x>=10) write(x/10);
		putchar((x%10)+'0');
	}

}

using namespace StandardIO;

namespace Solve {
	
	const int N=500500;
	
	int n,m;
	int cnt;
	int head[N];
	struct node {
		int to,next;
	} edge[N<<1];
	int fa[N][31],dep[N];
	
	inline void add (int a,int b) {
		edge[++cnt].to=b,edge[cnt].next=head[a],head[a]=cnt;
	}
	void dfs (int now,int f) {
		fa[now][0]=f,dep[now]=dep[f]+1;
		for (register int i=1; (1<<i)<=dep[now]; ++i) {
			fa[now][i]=fa[fa[now][i-1]][i-1];
		}
		for (register int i=head[now]; i; i=edge[i].next) {
			int to=edge[i].to;
			if (dep[to]) continue;
			dfs(to,now);
		}
	}
	inline int lca (int x,int y) {
		if (dep[x]>dep[y]) swap(x,y);
		for (register int i=20; i>=0; --i) {
			if (dep[y]>=dep[x]+(1<<i)) y=fa[y][i];
		}
		if (x==y) return x;
		for (register int i=20; i>=0; --i) {
			if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
		}
		return fa[x][0];
	}

	inline void Main () {
		read(n),read(m);
		for (register int i=1; i<=n-1; ++i) {
			int a,b;
			read(a),read(b);
			add(a,b),add(b,a);
		}
		dfs(1,0);
		for (register int i=1; i<=m; ++i) {
			int a,b,c,ab,bc,ca,ans,dis;
			read(a),read(b),read(c);
			ab=lca(a,b),bc=lca(b,c),ca=lca(c,a);
			if (ab==bc) ans=ca;
			else if (ab==ca) ans=bc;
			else if (bc==ca) ans=ab;
			dis=dep[a]+dep[b]+dep[c]-dep[ab]-dep[bc]-dep[ca];
			write(ans),putchar(' '),write(dis),putchar('
');
		}
	}

}

int main () {
	Solve::Main();
}
原文地址:https://www.cnblogs.com/ilverene/p/11206214.html