题目链接:https://www.luogu.com.cn/problem/P1099
用两边DFS求出直径,用f记录fa,这样就记录下了直径的路径,将直径上的点vis标记为1。然后枚举左右端点(或者尺取法,但不会)。
贪心考虑对答案的贡献:
假设直径的两个端点为a和b,当前左右端点为i,j,
对答案的贡献只有a,i之间的距离、b,j之间的距离、i,j中每个节点的子树的最远距离。
那么可以对于直径上的每一个节点做一次关于它的子树(除直径)的DFS,找出子树最远距离,存在dt[]中。注意不要每更换一次左右端点就重新求一遍,因为vis
会影响(坑)。
AC代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<queue> 5 using namespace std; 6 int n,s; 7 const int N=400; 8 int tot,ans=0x7f7f7f7f,anss,ansss; 9 int head[N]; 10 int f[N],maxd,dis[N],diss[N],dt[N]; 11 int p,vis[N]; 12 struct node{ 13 int to,next,w; 14 }edge[N<<1]; 15 void add(int u,int v,int w){ 16 edge[tot].to=v; 17 edge[tot].next=head[u]; 18 edge[tot].w=w; 19 head[u]=tot++; 20 } 21 void DFS(int u,int fa){ 22 f[u]=fa; 23 if(maxd<dis[u]){ 24 maxd=dis[u]; 25 p=u; 26 } 27 for(int i=head[u];i!=-1;i=edge[i].next){ 28 int v=edge[i].to; 29 if(vis[v]||v==fa) continue; 30 vis[v]=1; 31 dis[v]=dis[u]+edge[i].w; 32 DFS(v,u); 33 } 34 } 35 void DFSD(int u){ 36 if(diss[u]>maxd) maxd=diss[u]; 37 for(int i=head[u];i!=-1;i=edge[i].next){ 38 int v=edge[i].to; 39 if(vis[v]) continue; 40 vis[v]=1; 41 diss[v]=diss[u]+edge[i].w; 42 DFSD(v); 43 } 44 } 45 int main(){ 46 memset(head,-1,sizeof(head)); 47 memset(f,-1,sizeof(f)); 48 scanf("%d%d",&n,&s); 49 for(int i=1;i<n;i++){ 50 int u,v,w; 51 scanf("%d%d%d",&u,&v,&w); 52 add(u,v,w); add(v,u,w); 53 } 54 DFS(1,-1); 55 memset(dis,0,sizeof(dis)); 56 memset(f,-1,sizeof(f)); 57 memset(vis,0,sizeof(vis)); 58 int a=p; 59 maxd=0; 60 DFS(p,-1); 61 int b=p; 62 memset(vis,0,sizeof(vis)); 63 for(int i=b;i!=-1;i=f[i]) vis[i]=1; 64 for(int i=b;i!=-1;i=f[i]){ 65 for(int j=i;j!=-1&&dis[i]-dis[j]<=s;j=f[j]){ 66 anss=max(dis[b]-dis[i],dis[j]); 67 for(int k=i;k!=f[j];k=f[k]){ 68 maxd=0; 69 if(dt[k]) maxd=dt[k]; 70 else { DFSD(k); dt[k]=maxd;} 71 anss=max(anss,maxd); 72 } 73 ans=min(ans,anss); 74 } 75 } 76 printf("%d",ans); 77 return 0; 78 }