[Accumulation Degree]题解

换根dp板子题,首先,我们要想想如果根为1时,1的答案

我们设\(dp[i]\)表示以\(i\)为根子树的中,若\(i\)有无限流量,i点能往下流的最大流量。

我们不难推出式子\(dp[i]=\sum_{v\in son(i)}min(dp[v],w(u->v))\)

意义就是,我们知道一个儿子v可以向下流的最大流量是\(dp[v]\),我们最多可以向儿子v流\(w(u->v)\)的流量,所以我们最多向该儿子流\(min(dp[v],w(u->v))\)的流量,所有儿子的这个值的和就是\(dp[i]\)

特别的,若i是叶子的话,则\(dp[i]=+oo\)

现在,考虑换根dp

现在,根据换根dp的套路,假设我们要把根\(x\)换成\(x\)的一个儿子\(y\)

那么,我们画一个图:

现在我们要把根\(x\)换成\(y\),图就变成了

注意到,我们设的dp是,关于\(i\)的子树的,而在换根的过程中,只有\(x\)和\(y\)的子树分别减少和增加了一个儿子,所以,我们只需要将x和y的dp值分别减少和增加变化的值即可将所有的dp值改成以y为根的dp值,即:

\(dp[x]-=min(dp[y],w(x->y))[减去y对x的贡献],dp[y]+=min(dp[x],w(y->x))[加上x对y的贡献]\)【注意,顺序不能反】

同样,我们再考虑下特殊的\(y\)为叶子节点的情况

如果\(y\)是叶子节点,那么图一定是这样:

我们发现,这个时候,\(y\)的流量只能流向\(x\),所以\(y\)对答案的贡献是\(min(dp[x],w(y->x))\),所以,我们只要把这个值和ans取max即可~【因为我们一开始\(dp[y]=+oo\),如果直接算的话会出错】

当然,我们注意到\(dp[x]\)一定会将流量\(w(x->y)\)流向y,所以一定满足此时\(dp[x]>=w(y->x)\),所以我们直接把\(w(y->x)\)和ans取max即可

至此,我们就成功用\(O(1)\)将根x换成了它的一个儿子y,我们如果直接dfs一遍的话就可以\(O(n)\)求出以任意点为根(源点)时,整个树能流的最大流量,我们直接将答案和这些值取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;
}

原文地址:https://www.cnblogs.com/ThinkofBlank/p/12690205.html

时间: 2024-11-10 13:38:18

[Accumulation Degree]题解的相关文章

Palindrome Degree题解

Palindrome Degree题解 其实是道水题, 但是我太弱了!!! 开始想着如何判断后缀是回文, 屈辱看题解后发现, 只要判断前缀,然后判断后缀的反向是否与前缀相等即可, 但是我居然将kmp与回文弄混了,直接判前后缀相不相等, 太弱了!!败犬的哀嚎 #include<bits/stdc++.h> #define ull unsigned long long using namespace std; const int N=5e6+7; int n,t,f[N],ans=0; ull b

题解 poj3585 Accumulation Degree (树形dp)(二次扫描和换根法)

写一篇题解,以纪念调了一个小时的经历(就是因为边的数组没有乘2 phhhh QAQ) 题目 题目大意:找一个点使得从这个点出发作为源点,流出的流量最大,输出这个最大的流量. 以这道题来介绍二次扫描和换根法 作为一道不定根的树形DP,如果直接对每个点进行DP,可能时间会炸掉 但是,优秀的二次换根和扫描法可以再O(n^2)内解决问题. 二次扫描的含义:(来自lyd 算法竞赛进阶指南) 第一次扫描:任选一个节点为根节点(我会选1)在树上进行树形DP,在回溯时,从儿子节点向父节点(从底向上)进行状态转移

【POJ 3585】Accumulation Degree

[原题题面]传送门 [题解大意] 乍一看感觉以为网络流表示一点都不会不会不会. 然后发现可以树形dp搞一下. 再然后知道了换根dp. 设d[x]表示以x为根的子树中把x做为源点,从x出发流向子树的流量最大是多少. d[x] += min(d[y],z);(deg[y]!=1) d[x] = z; (deg[y]==1) f[x]表示以x做为源点,从x出发流向整个水系的流量最大是多少. f[y] = d[y] + min(f[x] - min(d[y],z));(deg[x]!=1) f[y] =

POJ3585 Accumulation Degree 【树形dp】

题目链接 POJ3585 题解 -二次扫描与换根法- 对于这样一个无根树的树形dp 我们先任选一根进行一次树形dp 然后再扫一遍通过计算得出每个点为根时的答案 #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #define LL long long int #define Redge(u) for (int k =

[POJ 3585] Accumulation Degree

[题目链接] http://poj.org/problem?id=3585 [算法] 树形DP--二次扫描与换根法 [代码] #include <algorithm> #include <bitset> #include <cctype> #include <cerrno> #include <clocale> #include <cmath> #include <complex> #include <cstdio&

[POJ3585]Accumulation Degree

题面 \(\text{Solution:}\) 有些题目不仅让我们做树型 \(\text{dp}\) ,而且还让我们换每个根分别做一次, 然后这样就愉快的 \(\text{TLE}\) 了,所以我们要用一种方法快速知道所有根的答案. 二次扫描与换根法: 就是先选任意点作根做一遍 \(\text{dp}\) ,求出相关信息,然后再从根往下 \(\text{dfs}\) ,对每一个节点往下走之前进行自顶向下的推导,计算出 "换根" 后的解. 就这题而言就是用父亲的换根后的答案来跟新自己换根

poj3585 Accumulation Degree(换根dp)

传送门 换根dp板子题(板子型选手 题意: 一棵树确定源点和汇点找到最大的流量(拿出一整套最大瘤板子orz const int maxn=2e5+10; int head[maxn],tot; struct node { int nt,to;long long w; }q[2*maxn]; long long dp[maxn];int cnt[maxn]; void insert(int u,int v,long long w) { q[tot].nt=head[u];q[tot].w=w;q[

NC201400 树学题解

一.题解 ? 这道题又是一道换根dp板子题,代码结构与 Accumulation Degree 这道题基本一致,唯一不同的就是转移了[不过转移的时候,因为方程的原因不需要特殊考虑叶节点] ? 我们先套路的设\(dp[i]\)表示以\(i\)为根的子树中,所有点的深度和,现在,我们来想想转移. ? 我们发现,如果我们要从\(i\)的一个儿子v转移到i的话,以v为根的子树中的所有节点的深度都加了1,现在我们就不能够直接求值了,怎么办呢?很简单,我们发现,既然以v为根的子树中的所有节点的深度都加了1,

换根DP

换根dp的通法:1.第一次扫描时,任选一个点为根,在"有根树"上执行一次树形DP,也就在回溯时发生的,自底向上的状态转移. 2.第二次扫描时,从刚才选出的根出发,对整棵树执行一次dfs,在每次递归前进行自上向下的推导,计算出换根后的解. 1.POJ3585 Accumulation Degree dp[i]以i为根的子树中,把i作为源点的最大流量 转移\(dp[x]=\sum_{y\epsilon son(x)}^{}\left\{\begin{matrix} min(dp[y],le