(暑假的CF模拟赛)
这是我感觉最难的题
一开始以为要先跑一遍树的直径,然后在剩下可以选的点里跑最长的。
经过几分钟沉思后发现这方法不对……
例如:
考虑换一种方法,枚举一个断边,将原树分开的两子树分别跑直径,然后直径相乘就可以求出答案。
(求直径我写的dp,赛事遗忘bfs求直径如何打)
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; long long head[305],ans,a,b,fk; struct hehe { long long w,syg; }lsqxx[605]; void add(long long t,long long w) { ans++; lsqxx[ans].w=w; lsqxx[ans].syg=head[t]; head[t]=ans; } long long n,zc; long long zj(long long wz,long long bb)//求直径函数 { long long daan=0,cd1=0,cd2=0;//cd1,cd2分别代表子树中深的最大值和次大值 for(int i=head[wz];i!=0;i=lsqxx[i].syg) { if(lsqxx[i].w==bb) { continue; } daan=max(daan,zj(lsqxx[i].w,wz));//具体思想,zc为向下搜后的最大值,如果比现在的最大值大,就替换,现在的次大值变为之前的最大值 if(zc>cd1) { cd2=cd1; cd1=zc; }else { cd2=max(cd2,zc);//如果zc没最大值大,就看看能否替换次大值 } } daan=max(daan,cd1+cd2);//然后就可以求出直径 zc=cd1+1; return daan; } int main() { scanf("%lld",&n); for(int i=1;i<n;i++) { scanf("%lld%lld",&a,&b); add(a,b); add(b,a); } for(int i=1;i<=n;i++) { for(int j=head[i];j!=0;j=lsqxx[j].syg)//断边 { zc=0; long long aa=zj(i,lsqxx[j].w); zc=0; long long aaa=zj(lsqxx[j].w,i); fk=max(fk,aa*aaa); } } printf("%lld ",fk); return 0; }