题目描述
调查兵团第56次壁外调查将对巨木之森展开,已知巨木之森共有n块区域和n-1条道路,保证这n块区域联通。为了调查结果尽可能准确,兵团会派遣若干支小分队调查巨木之森,派出的小分队需满足以下规定:

1.每支小分队都可以选择从任意一块区域出发,但各支小分队的出发区域必须互不相同。

2.每支小分队都必须遍历完nn块区域。

3.每支小分队的物资消耗量为其遍历完nn块区域的路程和。

现已知调查兵团的物资总量为mm,请问最多能派遣多少支小分队参与调查?

思路:对于树来说,遍历所有点回到出发点每条边要经过两边,花费是所有边的权值和乘以二假设为sum。然后小分队从某一点出发到达某个目的点,两点之间最短路上的边只需要经过一次,所以花费就是sum减去最短路上边的权值,为使花费最少,就需要找到距离该点最远的点也就是树的直径的某个端点。那么做法就出来了,找到树的直径的端点(先从1搜一遍找到最远的端点,再从该点搜一遍找到最远的端点,这两个端点的最短路就是直径),然后枚举所有点,求出每一点的最小花费存入数组,再sort一遍,找到最小的那几个花费,就是答案了。

代码:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
struct Node{
    int v;
    long long w;
    Node(int vv,long long ww):v(vv),w(ww){}
};
int n;
long long m;
vector<Node>edge[100005];
long long dist[2][100005];
void dfs(int u,int id,int pre,long long dis)
{
    dist[id][u]=dis;
    for(int i=0;i<edge[u].size();i++){
        int v=edge[u][i].v;
        if(v==pre)continue;
        dfs(v,id,u,dis+edge[u][i].w);
    }
}
int main()
{
    scanf("%d%lld",&n,&m);
    long long sum=0;
    for(int i=0;i<n-1;i++){
        int u,v;
        long long w;
        scanf("%d%d%lld",&u,&v,&w);
        edge[u].push_back(Node(v,w));
        edge[v].push_back(Node(u,w));
        sum+=w;
    }
    sum*=2;
    int l,r;
    long long temp=-1;
    dist[0][1]=0;
    dfs(1,0,0,0);
    for(int i=1;i<=n;i++)if(dist[0][i]>temp)temp=dist[0][i],l=i;
    dist[0][l]=0,temp=-1;
    dfs(l,0,0,0);
    for(int i=1;i<=n;i++)if(dist[0][i]>temp)temp=dist[0][i],r=i;
    dist[1][r]=0;
    dfs(r,1,0,0);
    int i;
    long long t=0,a[100005];
    for(i=1;i<=n;i++){
        a[i]=sum-max(dist[0][i],dist[1][i]);
        //printf("%lld\n",a[i]);
    }
    sort(a+1,a+n+1);
    for(i=1;i<=n;i++)
    {
        t+=a[i];
        if(t>m)break;
    }
    printf("%d\n",i-1);
    return 0;
}