題解 CF1294F 【Three Paths on a Tree】

$$ Preface $$c++

打比賽的時候先開了 F 題(霧spa

而後一眼看出 F 題結論,最後竟然由於沒有判重,交了三次才過。 $$ Description $$ 給出一棵無權樹(可理解爲邊權爲 $1$ ),你須要選取三個點 $a,b,c$ ,最大化 $a,b$ 和 $b,c$ 和 $a,c$ 的簡單路徑的並集的長度。code

輸出這個最大長度和 $a,b,c$ 。 $$ Solution $$ 有一個結論:ip

一定會有一組最優解,使得 $a,b$ 是樹直徑上的端點。get

這個結論我如今暫時不會證實,你們能夠去看看其餘 $dalao$ 的證實或是本身給出證實 $>v<$ 。string

$~$it

那咱們能夠套路地去把樹直徑兩端點求出來,這裏不推薦用 樹形dp ,推薦你們用 兩次搜索 求出樹直徑端點。io

肯定了 $a,b$ ,接下來咱們只要去找到最優的 $c$ ,就能夠最大化答案了。class

此時咱們注意到:$a,b$ 和 $b,c$ 和 $a,c$ 的簡單路徑的並集的長度其實就是 $\frac{dis(a,b)+dis(b,c)+dis(a,c)}{2}$ 。搜索

此時 $dis(a,b)$ 已經肯定了,當 $dis(b,c)+dis(a,c)$ 的值取到最大,那麼整個式子取最大。

把 $a,b$ 到全部點的簡單路徑距離求出來,去枚舉這個最優的 $c$ 便可,枚舉的過程當中記得判與 $a,b$ 相同的狀況。 $$ Code $$

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue> 

#define RI register int

using namespace std;

inline int read()
{
	int x=0,f=1;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=-f;s=getchar();}
	while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
	return x*f;
}

const int N=200100,M=400100;

int n;

int tot,head[N],ver[M],edge[M],Next[M];

void add(int u,int v,int w)
{
	ver[++tot]=v;    edge[tot]=w;     Next[tot]=head[u];    head[u]=tot;
}

int d[N],vis[N];

int pos;

void bfs(int sta)
{
	memset(d,0,sizeof(d));
	memset(vis,0,sizeof(vis));

	queue<int>q;

	q.push(sta);
	vis[sta]=1;

	while(q.size())
	{
		int u=q.front();q.pop();
		for(RI i=head[u];i;i=Next[i])
		{
			int v=ver[i],w=edge[i];
			if(vis[v])continue;
			d[v]=d[u]+w;
			vis[v]=1;
			if(d[v]>d[pos])pos=v;
			q.push(v);
		}
	}
}

int p1,p2;
int ans;

int tmp1[N],tmp2[N];

int main()
{
	n=read();

	for(RI i=1;i<n;i++)
	{
		int u=read(),v=read();
		add(u,v,1),add(v,u,1);
	}

	bfs(1);
	p1=pos;

	bfs(p1);
	p2=pos;

	for(RI i=1;i<=n;i++)
		tmp1[i]=d[i];

	bfs(p2);

	for(RI i=1;i<=n;i++)
		tmp2[i]=d[i];

	pos=0;
	for(RI i=1;i<=n;i++)
		if(tmp1[i]+tmp2[i]>tmp1[pos]+tmp2[pos]&&i!=p1&&i!=p2)pos=i;

	ans=(tmp1[p2]+tmp1[pos]+tmp2[pos])/2;

	printf("%d\n",ans);
	printf("%d %d %d\n",p1,p2,pos);

	return 0;
}

$$ Thanks \ for \ watching $$

相關文章
相關標籤/搜索