poj3585树最大流——换根法

题目:http://poj.org/problem?id=3585

二次扫描与换根法,一次dfs求出以某个节点为根的相关值,再dfs遍历一遍树,根据之前的值换根取最大值为答案。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,head[200005],ct,d[200005],f[200005],deg[200005],ans,t;
bool vis[200005];
struct N{
    int to,next,w;
}edge[400005];
void add(int x,int y,int z)
{
    ct++;
    edge[ct].to=y;
    edge[ct].next=head[x];
    edge[ct].w=z;
    head[x]=ct;
}
void dp(int x)
{
    vis[x]=1;
    for(int i=head[x];i;i=edge[i].next)
    {
        int u=edge[i].to;
        if(vis[u])continue;
        dp(u);
        if(deg[u]==1)d[x]+=edge[i].w;
        else d[x]+=min(d[u],edge[i].w);
    }
}
void dfs(int pre,int w,int x)
{
    vis[x]=1;
    if(deg[pre]==1)f[x]=d[x]+w;
    else f[x]=d[x]+min(f[pre]-min(d[x],w),w);
    ans=max(ans,f[x]);
    for(int i=head[x];i;i=edge[i].next)
    {
        int u=edge[i].to;
        if(vis[u])continue;
        dfs(x,edge[i].w,u);
    }
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        memset(deg,0,sizeof deg);
        memset(f,0,sizeof f);
        memset(d,0,sizeof d);
        memset(head,0,sizeof head);
        memset(vis,0,sizeof vis);
        ans=0;ct=0;
        scanf("%d",&n);
        int a,b,c;
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,c);
            add(b,a,c);
            deg[a]++;deg[b]++;
        }
        dp(1);
        f[1]=d[1];
        memset(vis,0,sizeof vis);
        dfs(0,0,1);
        printf("%d\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Zinn/p/8605654.html

时间: 2024-10-28 21:06:05

poj3585树最大流——换根法的相关文章

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

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

cf219d 基础换根法

/*树形dp换根法*/ #include<bits/stdc++.h> using namespace std; #define maxn 200005 struct Edge{int to,nxt,flag;}edge[maxn<<1]; int root,n,s,t,head[maxn],tot,dp[maxn]; void init(){ memset(head,-1,sizeof head); tot=0; } void addedge(int u,int v,int fl

【51Nod1405】树上距离和 二次扫描与换根法

题目大意:给定一棵 N 个点的边权均为 1 的树,依次输出每个点到其他各个点的距离和. 题解:首先任意选定一个节点为根节点,比如 1,第一遍 dfs 遍历树求出子树大小.树上前缀和.第二遍 dfs 遍历这棵树,求出各个点的距离和. 对于遍历到的任意一个节点 i,对于与之相邻的节点 j 来说,答案贡献由 i 到 j 转移首先减小了 \(size[j]*1\),同时增加了 \((n-size[j])*1\),因此可以直接得到\(dp[j]=dp[i]+n-size[j]*2\). 代码如下 #inc

树形dp经典换根法——cf1187E

假设以u为根时结果是tot,现在转换到了以u的儿子v为根,那么结果变成了tot-size[v]+(sizetot-size[v]) 根据这个转移方程,先求出以1为根的tot,然后dfs一次转移即可 #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; #define ll long long #define N 200005

遥远的国度(树链剖分,换根)

遥远的国度 题目描述 zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度.当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn完成任务后才能进入遥远的国度继续追杀. 问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连接且这些城市构成了一颗树.这个国度有一个首都,我们可以把这个首都看做整棵树的根,但遥远的国度比较奇怪,首都是随时有可能变为另外一个城市的.遥远的国度的每个城市有一个防御值,有些时候RapiD会使

肥宅快乐树 换根+树形DP/dfs

肥宅快乐树是一棵神秘而巨大的树,它长有许多枝条和节点,每条枝连接树中两个节点,每个节点上都长有一瓶肥宅快乐水. 何老板是肥宅快乐水的资深爱好者.历经艰难,他终于找到了这棵传说中的快乐树.他想要获取树上所有的快乐水,迫不及待地想从树根往树上爬. 每经过一条树枝都会耗费一定体力.而且快乐树自带防御功能,即每条枝上都有一个一次性陷阱,一旦踏上该枝,何老板就会被立即弹射回地面,他得重新从根往上爬. (注1:一次性陷阱是指,陷阱只在第一次经过该枝时有效) (注2:从i号点回到i的父亲节点,不耗费体力) 每

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[

POJ3585 换根模板

题意:机翻? 好,假装各位都已经看懂了题. 首先是暴力,枚举每一个点作为根,然后每次做一个树上DP,复杂度O(n2),T掉: 然后,我们考虑怎样优化. 假设我们已经求出了x的答案,对与每一个它的子节点, 我们注意到其实当我们换其子节点y为根时,y的子树贡献是已知的. 只需考虑另外一侧的贡献之间, 同时又注意到,除y以外的对x的贡献就是x的答案减掉y对它的贡献,也是一定的. 所以只有x会影响到y,直接根据限制在y和x之间转移一下就好了. #include <cstdio> #include &l

换根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