换根dp板子题,首先,我们要想想如果根为1时,1的答案
我们设表示以为根子树的中,若有无限流量,i点能往下流的最大流量。
我们不难推出式子
意义就是,我们知道一个儿子v可以向下流的最大流量是,我们最多可以向儿子v流的流量,所以我们最多向该儿子流的流量,所有儿子的这个值的和就是了
特别的,若i是叶子的话,则
现在,考虑换根dp
现在,根据换根dp的套路,假设我们要把根换成的一个儿子
那么,我们画一个图:
现在我们要把根换成,图就变成了
注意到,我们设的dp是,关于的子树的,而在换根的过程中,只有和的子树分别减少和增加了一个儿子,所以,我们只需要将x和y的dp值分别减少和增加变化的值即可将所有的dp值改成以y为根的dp值,即:
【注意,顺序不能反】
同样,我们再考虑下特殊的为叶子节点的情况
如果是叶子节点,那么图一定是这样:
我们发现,这个时候,的流量只能流向,所以对答案的贡献是,所以,我们只要把这个值和ans取max即可~【因为我们一开始,如果直接算的话会出错】
当然,我们注意到一定会将流量流向y,所以一定满足此时,所以我们直接把和ans取max即可
至此,我们就成功用将根x换成了它的一个儿子y,我们如果直接dfs一遍的话就可以求出以任意点为根(源点)时,整个树能流的最大流量,我们直接将答案和这些值取max即可
代码:
#include<bits/stdc++.h> using namespace std; const int N=2e5+1; #define int long long struct node{ int v,w,nex; }t[N<<1]; int las[N],len; int dp[N],ans; bool flag[N]; inline void add(int u,int v,int w){ t[++len]=(node){v,w,las[u]},las[u]=len; } inline void dfs1(int now,int fa){ flag[now]=0; for(int i=las[now];i;i=t[i].nex){ int v=t[i].v,w=t[i].w; if(v!=fa){ flag[now]=1; dfs1(v,now); dp[now]+=min(dp[v],w); } } if(!flag[now]){ dp[now]=2e9; } } inline void dfs2(int now,int fa){ ans=max(ans,dp[now]); for(int i=las[now];i;i=t[i].nex){ int v=t[i].v,w=t[i].w; if(v!=fa){ int pas1=dp[now],pas2=dp[v];//偷懒,直接将原来的值存下来,dfs回来后直接改回去即可 if(!flag[v]){ ans=max(ans,w); continue; } dp[now]-=min(dp[v],w);dp[v]+=min(dp[now],w); dfs2(v,now); dp[now]=pas1,dp[v]=pas2; } } } signed main(){ int T; scanf("%lld",&T); while(T--){ memset(dp,0,sizeof(dp)); memset(las,0,sizeof(las)),len=0; int n;ans=0; scanf("%lld",&n); for(int i=1;i<n;++i){ int u,v,w; scanf("%lld%lld%lld",&u,&v,&w); add(u,v,w),add(v,u,w); } dfs1(1,1);dfs2(1,1); printf("%lld\n",ans); } return 0; }