A 巨木之森

分析

先有一个思路,考虑一个点的答案记录。因为除了起始节点之间的路径,其它的路径都要经过两次。所以一个节点的答案为 表示总路径的长度, 表示从节点 开始的最长路径。现在就只需要考虑快速算出所有的节点的答案,这里我们抛出一个引理:对于树上的每个节点,距离他最远的点一定是树的直径的某一个端点。这里也非常好证明,考虑反证法,若不为直径一端点,考虑两条路径相交、不相交两种情况,可以证明出一定有比直径更长的路径。所以只要求出一个节点到直径端点的距离就可以了。时间复杂度为

代码

#include<bits/stdc++.h>
using namespace std;
#define LL long long 
LL read() {LL x;scanf("%lld",&x);return x;}
const int N = 1e5+100;
vector<LL> W[N],G[N];
LL ans[N];
LL f[N],dis[N],dis2[N],dep[N],rt,rt2,n,m,sum;
void dfs(int x,int fa,LL Dep) {
    dep[x] = Dep;
    for(int i = 0;i < G[x].size();i++) {
        int y = G[x][i];
        if(y == fa) continue;
        dfs(y,x,Dep+W[x][i]);
    }
    if(dep[rt] < dep[x]) rt = x;
}
void dfs1(int x,int fa,LL Dep) {
    dis[x] = Dep;
    for(int i = 0;i < G[x].size();i++) {
        int y = G[x][i];
        if(y == fa) continue;
        dfs1(y,x,Dep+W[x][i]);
    }    
    if(dis[rt2] < dis[x]) rt2 = x;
}
void dfs2(int x,int fa,LL Dep) {
    dis2[x] = Dep;
    for(int i = 0;i < G[x].size();i++) {
        int y = G[x][i];
        if(y == fa) continue;
        dfs2(y,x,Dep+W[x][i]);
    }    
}
int main()
{
    n = read();m = read();
    for(int i = 1;i < n;i++) {
        LL a = read(),b = read(),c = read();sum += c;
        G[a].push_back(b);G[b].push_back(a);
        W[a].push_back(c);W[b].push_back(c);
    }
    dfs(1,0,0);dfs1(rt,0,0);dfs2(rt2,0,0);
    for(int i = 1;i <= n;i++) ans[i] = sum*2-max(dis[i],dis2[i]);
    sort(ans+1,ans+1+n);
    LL Ans = 1;
    for(;Ans <= n;Ans++){
        if(m < ans[Ans]) break;
        m -= ans[Ans];
    }  
    cout << Ans-1 << endl;
    return 0;
}