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的点做为根。

浙公网安备 33010602011771号