zoukankan      html  css  js  c++  java
  • [Accumulation Degree]题解

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

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

    我们不难推出式子(dp[i]=sum_{vin 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
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    Spring 定时任务 @Scheduled cron表达式
    github 获取生成 token 的方法
    jQuery 获取当前日期及前一周或者后一周的日期
    jQuery 复选框 checkbox 取值和赋值
    git clone前后端项目之后的配置操作(一定不要忘记)
    springboo的postman后端接口测试流程详解
    Navicat Premium 15 永久破解激活工具及安装教程(亲测可用)
    企业微信的单点登录适配
    git的可视化和命令行操作步骤
    CMD中键入javac显示 javac不是内部或者外部命令
  • 原文地址:https://www.cnblogs.com/ThinkofBlank/p/12690205.html
Copyright © 2011-2022 走看看