1. 程式人生 > >洛谷P4281【AHOI2008】緊急集合/聚會

洛谷P4281【AHOI2008】緊急集合/聚會

題目傳送門

這道題目有兩個版本的題面( l o l lol ),不過題目的意思是一樣的。
這道題給出了一棵 n n

個點的樹和 m m 個詢問,對於每個詢問給出三個點,求這棵樹上到這三個點距離之和最小的點以及最小距離和。

我們先來考慮一個簡化版本:每次不是詢問三個點,而是兩個。那麼答案顯而易見:合法的點為這兩個點的 l c a

lca 到這兩個點路徑上的任意一點,最短距離和也就是這兩個點的距離。

好了,那麼我們回到這個問題,給你三個點怎麼辦。根據我們剛剛得出的結論,答案就在這三個 l c a lca 到這三個點的路徑上。而 l

c a lca 有一個性質:對樹上任意三個點兩兩求 l c a lca ,這三個 l c a lca 至少有兩個相同,且如果有兩個 l c a lca 相同,另外一個 l c a lca 的深度一定是最大的。

我們設詢問的三個點分別為 a a b b c c l c a ( a , b ) lca(a,b) x x l c a ( a , c ) lca(a,c) l c a ( b , c ) lca(b,c) 均為 y y 。那麼答案所求的點一定是 x x 。為什麼呢?首先我們不難發現, x x 一定在 y y a a b b 的路徑上。令 a n s ans 表示將 x x 作為集合點時的距離和, d [ i ] d[i] 表示點 i i 的深度,那麼顯然 a n s = a ans=a b b 的距離 + c +c x x 的距離 = d [ a ] + d [ b ] 2 d [ x ] + d [ x ] + d [ c ] 2 d [ y ] = d [ a ] + d [ b ] + d [ c ] d [ x ] 2 d [ y ] =d[a]+d[b]-2d[x]+d[x]+d[c]-2d[y]=d[a]+d[b]+d[c]-d[x]-2d[y]。 我們剛剛說過答案點在 y y a a b b c c 的路徑上,那麼我們分類討論一下:

1、如果答案點 p p a a b b 的路徑上,那麼距離和為 d [ a ] + d [ b ] 2 d [ x ] + d [ p ] + d [ c ] 2 d [ y ] d[a]+d[b]-2d[x]+d[p]+d[c]-2d[y] ,對比上面 a n s ans 的式子可以發現這個距離和一定大於 a n s ans ,因為顯然 d [ p ] > d [ x ] d[p]>d[x]

2、如果答案點 p p c c a a b b 的路徑上,那麼距離和為 d [ a ] + d [ b ] 2 d [ p ] + d [ p ] + d [ c ] 2 d [ y ] = d [ a ] + d [ b ] + d [ c ] d [ p ] 2 d [ y ] d[a]+d[b]-2d[p]+d[p]+d[c]-2d[y]=d[a]+d[b]+d[c]-d[p]-2d[y] ,對比上面的式子,因為 d [ p ] < d [ x ] d[p]<d[x] ,所以這個距離和也大於 a n s ans

現在我們證明了答案一定為 x x 。如果我們再觀察一下 a n s ans 的式子可以發現,因為 y y 既為 l c a ( a , c ) lca(a,c) ,又為 l c a ( b , c ) lca(b,c) ,所以 a n s ans 可以轉換成 d [ a ] + d [ b ] + d [ c ] d [ l c a ( a , b ) ] d [ l c a ( a , c ) ] d [ l c a ( b , c ) ] d[a]+d[b]+d[c]-d[lca(a,b)]-d[lca(a,c)]-d[lca(b,c)] ,這樣就免去了一個分類討論。

另外就是這道題的資料範圍比較大,推薦樹剖求 l c a lca ,倍增的話可能需要卡一下常數(至少卡常之後可以在洛谷過去)

倍增:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=500010;
int n,m,top,h[N],to[N<<1],pre[N<<1],d[N],fa[N][22];
int read()//快讀 
{
	int sum=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')sum=(sum<<3)+(sum<<1)+ch-'0',ch=getchar();
	return sum;
}
void ins(int u,int v)//建邊 
{
	pre[++top]=h[u];h[u]=top;to[top]=v;
}
void dfs(int x)
{
	for(int i=h[x];i;i=pre[i])
	{
		int v=to