$$ 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 $$