zoukankan      html  css  js  c++  java
  • 换根dp

    感觉这类问题很少?算了,还是拿出来水一下吧qwq...


    首先来看一道例题:POJ3585

    一句话题意:树上任意源点多汇点最大流

    你看这不就是个最大流的板子题吗?我先建个图,然后跑最大流,然后,,,然后?

    然后

    All the elements are nonnegative integers no more than 200000.

    还多测... mdzz

    好了来讲换根吧。

    首先有一个暴力(dp),我们先考虑以(rt=1)的情况((1)为根),令(dp[x])表示(rt)为根时,直接往节点(x)灌水,然后向(x)的子树内流的最大流量

    有如下转移

    [dp[x] = sumlimits_{v in son_x} min{dp[v], w(x,v)} ]

    特别的,如果(v)(1)为根的情况下是叶节点的话,(dp[x] += w(x,v))(w(x,y))表示边((x,y))的容量)

    然后(O(n))枚举(rt),再(O(n))dp,就可以收获一个(n^2)的优秀做法。

    发现换根过后树的变化实际上没有那么大,我们令(f[x])表示(rt=x)时的(dp[x])

    考虑把(rt)换到(x)的一个儿子(v)上,由于(x)为根的时候(v)也是(x)的一个儿子,所以(v)会对(f[x])产生(min(dp[v],w(x,v)))的贡献

    那么(f[x]-min(dp[v],w(x,v)))就得到了除了子树(v)以外的部分,它在换根后会成为(v)的一棵子树

    所以(f[v] = dp[v]+minleft(w_{x,v},f[x]-min(dp[v],w_{x,v}) ight))(嘛...括号太多看不清楚...所以这里就直接(w_{x,v})了qwq)

    需要特判(x)只有(v)一个儿子的时候(f[v] = dp[v]+w_{x,v})(就直接从(v)流向(x)了)

    所以Code

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <iostream>
    #include <set>
    #include <map>
    #include <vector>
    #include <queue>
    using namespace std;
    #define fore(i,x) for(int i=head[x],v=e[i].to,w=e[i].w;i;i=e[i].nxt,v=e[i].to,w=e[i].w)
    const int N=3e5+10;
    struct edge
    {
        int to,nxt,w;
    }e[N<<1];
    int head[N],cnt;
    inline void ade(int x,int y,int w)
    {e[++cnt]=(edge){y,head[x],w},head[x]=cnt;}
    inline void addedge(int x,int y,int w)
    {ade(x,y,w),ade(y,x,w);}
    int n,sum[N],f[N],deg[N]; // 这里的sum就是上面的dp辣
    void dfs(int x,int prev)
    {
        fore(i,x) if(v!=prev)
        {
            dfs(v,x);
            if(deg[v]==1) sum[x]+=w;
            else sum[x]+=min(sum[v],w);
        }
    }
    void getans(int x,int prev)
    {
        fore(i,x) if(v!=prev)
        {
            if(deg[x]==1) f[v]=sum[v]+w;
            else f[v]=sum[v]+min(f[x]-min(sum[v],w),w);
            getans(v,x);
        }
    }
    void sol()
    {
        memset(deg,0,sizeof(deg));
        memset(sum,0,sizeof(sum));
        memset(f,0,sizeof(f));
        memset(head,0,sizeof(head));
        memset(e,0,sizeof(e));
        cnt=0;
        scanf("%d",&n);
        for(int x,y,w,i=1;i<n;i++)
        {
            scanf("%d%d%d",&x,&y,&w);
            addedge(x,y,w);
            ++deg[x],++deg[y];
        }
        dfs(1,0);
        f[1]=sum[1],getans(1,0);
        int ans=0;
        for(int i=1;i<=n;i++)ans=max(ans,f[i]);
        printf("%d
    ",ans);
    }
    int main()
    {
        int T; scanf("%d",&T); while(T--)sol();
        return 0;
    }
    

    于是再来看一道题:LuoguP3478

    同样换根,令(f[x])表示(x)为根时的所有节点的深度和。

    考虑将根换到(x)的一个儿子(v)上,那么对于子树(v)里面的节点,他们的深度会减少(1),其他节点深度会加上(1),即((size_v)表示(v)(1)为根时的子树大小)

    [f[v]=f[x]+n-size_v-size_v=f[x]+n-2 imes size_v ]

    先以(1)为根求出(size),然后换根就好了

    十年OI一场空,不开long long见祖宗

    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define fore(i,x) for(int i=head[x],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
    const int N=1e6+10;
    int n;
    struct edge
    {
        int to,nxt;
    }e[N<<1];
    int head[N],cnt=0;
    inline void ade(int x,int y)
    {e[++cnt]=(edge){y,head[x]},head[x]=cnt;}
    inline void addedge(int x,int y)
    {ade(x,y),ade(y,x);}
    ll f[N],dep[N];
    int siz[N];
    void dfs(int x,int prev,int deep)
    {
        dep[x]=deep,siz[x]=1;
        fore(i,x) if(v!=prev)
            dfs(v,x,deep+1),siz[x]+=siz[v];
    }
    void getans(int x,int prev)
    {
        fore(i,x) if(v!=prev)
        {
            f[v]=f[x]+n-2ll*siz[v];
            getans(v,x);
        }
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1,x,y;i<n;i++)
            scanf("%d%d",&x,&y),addedge(x,y);
        dfs(1,0,0); 
        for(int i=1;i<=n;i++) f[1]+=dep[i];
        getans(1,0);
        ll mx=0; int ans=0;
        for(int i=1;i<=n;i++)
            if(f[i]>mx) mx=f[i],ans=i;
        printf("%d
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    数据库的查询优化技术(转载)
    数据库查询优化方案(处理上百万级记录如何提高处理查询速度)(转载)
    oracle 游标使用详解
    MySQL-linux系统下面的配置文件
    git分支相关的命令
    让双网卡同时工作在内网和外网
    比较ping,tracert和pathping等命令之间的关系
    选择图片,获取图片
    导出功能代码
    什么是以太网??
  • 原文地址:https://www.cnblogs.com/wxq1229/p/12317448.html
Copyright © 2011-2022 走看看