思路:
一开始的想法就是直接每个点去dfs树形dp后取最大,复杂度n^2
看到数据范围后 想法直接见祖宗了 然后就去学了一下二次扫描和换根法。
简单介绍一下。
其实第一步就是上面说的dfs树形dp。
随便选取一个点为根,假设我们选1,则dp[root]表示以root为根的子树的最大流量。
对于root的子节点son
我们这样考虑,如果son的度为1,则dp[root] += e(root,son) 其中e(root,son)表示root到son这条河道的流量限制。
如果son的度不为1则dp[root] += min( dp[son],e(root,son) ) 什么意思呢 就是说我子树的流量可能允许很大,但是root到son这一条河道的允许通过量很小,对允许的量有了个限制,所以应该取最小值去加
这是第一次dfs 处理出来所有dp数组 表示以i为根节点的最大流量。
然后就是第二个dfs
用f[i]表示以i为源点的最大流量,那么刚才计算出来了dp[root]
f[root]显然等于dp[root]吧
第二个dfs主要是表示父节点root作为源点转移为子节点son作为源点快速计算出f[son]
那么son做为源点,流量有两个去处,一就是以son为子树的最大流量 也就是dp[son] 这个在第一个dfs里已经求过了
二就是root作为父节点时候除了给son的流量的剩余流量,对于root给除了son这个子树的流量的其他流量怎么算呢?
即f[root]-min(dp[son],e(root,son))这个就是root除去给son子树的流量的其他流量。
那么对于这些流量,他要从son流到其他地方,是不是只能经过son-root这条路 肯定是啊 我们把son和其子树作为一个整体节点看
整个图就剩下三个节点了 一个son 一个root 一个其他
那么son流到其他的流量就是min( e( root , son ) ,f[root] - min( dp[son] , e( root , son ) ) 从son到其他的地方去等价从其他地方到son来,我们已经知道了从其他的到root的流量,只要在跟从root->son这条河道允许的量比较一下即可。
所以对于root的度不为1,则有f[son] = dp[son] + min( e( root , son ) ,f[root] - min( dp[son] , e( root , son ) )
对于root的度为1,则f[son] = dp[son] + e( root , son )
#include<bits/stdc++.h> using namespace std; const int maxn=2e5+5; int dp[maxn],f[maxn]; bool vis[maxn]; int du[maxn]; struct node { int to,cost; }; vector<node> e[maxn]; void dfs1(int x){ vis[x]=1; dp[x]=0; for(auto it:e[x]){ int to=it.to,v=it.cost; if(vis[to]) continue; dfs1(to); if(du[to]==1) dp[x]+=v; else dp[x]+=min(dp[to],v); } } void dfs2(int x){ vis[x]=1; for(auto it:e[x]){ int to=it.to,v=it.cost; if(vis[to]) continue; if(du[x]==1) f[to]=dp[to]+v; else f[to]=dp[to]+min(f[x]-min(dp[to],v),v); dfs2(to); } } int main(){ int t;cin>>t; while(t--){ int n;cin>>n; for(int i=1;i<=n;i++) du[i]=vis[i]=0,e[i].clear(); for(int i=1;i<n;i++){ int x,y,d;cin>>x>>y>>d; du[x]++,du[y]++;; e[x].push_back({y,d}); e[y].push_back({x,d}); } dfs1(1); for(int i=1;i<=n;i++) vis[i]=0; f[1]=dp[1]; dfs2(1); int ans=0; for(int i=1;i<=n;i++) ans=max(ans,f[i]); cout<<ans<<endl; } return 0; }