P10974 Accumulation Degree 题解

其实很容易知道,一个点有两种方法流向叶子结点。

先上去,然后拐弯下来

直接下去。

我们考虑动态规划

我们定义 \(B(x)\) 为节点 \(x\) 到达以 \(x\) 为根的子树的叶子节点的最大流量。\(W(u,v)\) 意为连接$ u,v$ 两点的边的流量

很显然,每个 \(u\),及其 \(u\) 的所有子节点 \(v\), \(B(x)=\sum min(B(v),W(u,v))\)

我们 \(dp\) 计算每个节点的 \(B(x)\)

那么考虑对于每个节点\(u\),如何计算 \(A(u)\)

定义对于节点\(u\), 他的父节点记为 \(fa(u)\)

显然 \(A(u)=向下走的最大流量+向上走的最大流量=B(u)+min(A(fa(u)),min(u,fa(u)))\)

显然,这是可以递归处理的,不过要先处理父节点,即从深度由浅到深计算。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const ll inf=1e18;
const ll maxn=2e5+5;

inline ll read() {
    long long x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + (ch - '0');
        ch = getchar();
    }
    return x * f;
}

struct Edge{
    ll v,c,nxt;
};

ll deg[maxn];
ll T;
Edge e[maxn<<1];
ll head[maxn];
ll sz[maxn],fa[maxn],dp[maxn],a[maxn];
ll n,etot,ans;

void add_edge(ll u,ll v,ll c) {
    ++etot;
    e[etot]={v,c,head[u]};
    head[u]=etot;
}

void dfs(ll u,ll father) {
    sz[u]=1;
    fa[u]=father;
    for(ll i=head[u];i;i=e[i].nxt) {
        if(e[i].v==fa[u]) {
            continue;
        }
        dfs(e[i].v,u);
        sz[u]+=sz[e[i].v];
    }
    return;
}

void work(ll u) {
    if(sz[u]==1) {
        dp[u]=inf;
        return;
    }
    for(ll i=head[u];i;i=e[i].nxt) {
        if(e[i].v==fa[u]) {
            continue;
        }
        work(e[i].v);
        dp[u]+=min(dp[e[i].v],e[i].c);
    }
    return;
}

void solve(ll u) {
    a[u]=dp[u];
    for(ll i=head[u];i;i=e[i].nxt) {
        if(e[i].v!=fa[u]) {
            continue;
        }
        a[u]+=min(e[i].c,a[fa[u]]-min(dp[u],e[i].c));
    }
    for(ll i=head[u];i;i=e[i].nxt) {
        if(e[i].v==fa[u]) {
            continue;
        }
        solve(e[i].v);
    }
    return;
}

void task(){
    cin>>n;

    etot=0,ans=0;
    fill(sz+1,sz+2+n,0);
    fill(dp+1,dp+2+n,0);
    fill(a+1,a+2+n,0);
    fill(fa+1,fa+2+n,0);
    fill(head+1,head+2+n,0);
    fill(deg+1,deg+2+n,0);

    for(ll i=1;i<n;++i) {
        ll u,v,c;
        cin>>u>>v>>c;
        add_edge(u,v,c);
        add_edge(v,u,c);
        ++deg[v],++deg[u];
    }
    ll s=0;
    for(ll i=1;i<=n;++i) {
        if(deg[i]>1) {
            s=i;
            break;
        }
    }

    dfs(s,0);
    work(s);
    solve(s);

    for(ll i=1;i<=n;++i) {
        if(sz[i]==1) {
            a[i]-=inf;
        }
        ans=max(ans,a[i]);
    }

    printf("%lld\n",ans);
    return;
}

int main() {
    //freopen("input.txt","r",stdin);

    cin>>T;

    while(T--) {
        task();
    }

    return 0;
}

注意一下,如果默认1为根节点的话,容易把根节点1漏掉。(根节点的度数可能是1) 所以要找一个度数非1的点做为根。

posted @ 2026-08-06 13:39  txp2025  阅读(3)  评论(0)    收藏  举报