给一棵树,每条边上都有一个权值,去掉树上任意一条边之后,分成两个子树,两个子树的最长路与这条边上的权值相乘,的到一个乘积。问去掉那一条边可以使这个乘积最小。
首先找到树上的最长路,那么删边的时候有两种情况:
1. 这条边不是最长路上的边
2. 这条边是最长路上的边
对于第一种情况,很容易计算出乘积。
对于第二种情况,只需要计算出被分成的两个子树里面的最长路径长度即可,这个可以预处理一下。分析可以发现,这种情况下,两棵子树的最长路,一定是以整棵树的最长路的两个端点为起始点的,因此只需要预处理出所有点到两个端点的距离,然后根据删除的最长路边求两颗子树中的最大值即可。
所有预处理都是dfs……
My code:
#pragma comment(linker,"/STACK:102400000,102400000")
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100100
int n;
int head[N],cnt,len,ans,id;
int dis_s[N],dis_t[N],mx_s[N],mx_t[N];
struct Edge
{
int next,to,w,id;
}e[N*2];
void add_edge(int a,int b,int w,int id)
{
e[cnt].id=id;
e[cnt].to=b;
e[cnt].w=w;
e[cnt].next=head[a];
head[a]=cnt++;
}
void dfs(int u, int fa, int dis[])
{
for(int i = head[u]; i != -1; i = e[i].next)
{
int v=e[i].to;
if(v!=fa)
{dis[v]=dis[u]+1;dfs(v,u,dis);}
}
}
void get_max(int u, int fa, int mx[],int dis[])
{
mx[u]=dis[u];
for(int i = head[u]; i != -1; i = e[i].next)
{
int v=e[i].to;
if(v!=fa)
{
get_max(v,u,mx,dis);
mx[u]=max(mx[u],mx[v]);
}
}
}
void dfs2(int u, int fa)
{
for(int i = head[u]; i != -1; i = e[i].next)
{
int v=e[i].to;
if(v!=fa)
{
int w;
if(dis_s[u]+dis_t[v]+1==len) w=e[i].w*max(mx_s[u],mx_t[v]);
else w=e[i].w*len;
if(w<ans) {ans=w;id=e[i].id;}
else if(w==ans) id=min(id,e[i].id);
dfs2(v,u);
}
}
}
int main()
{
int T;scanf("%d",&T);
for(int g = 1; g <= T; g++)
{
scanf("%d",&n);
memset(head,-1,sizeof(head));
cnt=0;
for(int i = 1; i < n; i++)
{
int a,b,w;scanf("%d%d%d",&a,&b,&w);
add_edge(a,b,w,i);
add_edge(b,a,w,i);
}
dis_s[1]=0;
dfs(1,-1,dis_s);
int s=1;
for(int i = 1; i <= n; i++)if(dis_s[s]<dis_s[i])s=i;
dis_s[s]=0;
dfs(s,-1,dis_s);
len=dis_s[1];
int t=1;
for(int i = 1; i <= n; i++)if(dis_s[t]<dis_s[i]){t=i;len=dis_s[t];}
dis_t[t]=0;
dfs(t,-1,dis_t);
get_max(t,-1,mx_s,dis_s);
get_max(s,-1,mx_t,dis_t);
ans=0x7ffffff;
dfs2(s,-1);
printf("Case #%d: %d\n",g,id);
}
return 0;
}