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

写一篇题解,以纪念调了一个小时的经历(就是因为边的数组没有乘2 phhhh QAQ)



题目

题目大意:找一个点使得从这个点出发作为源点,流出的流量最大,输出这个最大的流量。

以这道题来介绍二次扫描和换根法

作为一道不定根的树形DP,如果直接对每个点进行DP,可能时间会炸掉

但是,优秀的二次换根和扫描法可以再O(n^2)内解决问题。

二次扫描的含义:(来自lyd 算法竞赛进阶指南)

第一次扫描:任选一个节点为根节点(我会选1)在树上进行树形DP,在回溯时,从儿子节点向父节点(从底向上)进行状态转移

第二次扫描:从刚才选的根出发,对树进行dfs,在每次递归前进行自顶向下的推导,计算"换根"后的解

在第一次扫描时,我们可以算出以节点u为根的子树中,从u流向其子树的最大流量

在第二次扫描时,我们通过dfs,可以自上而下的求出以节点u为根,从u流向整个流域(其子树)的最大流量

当我们从节点u到节点v时,已经求出F[u],而从u到v的流量为min(D[v],w(u,v)),所以从u流向v的其他部分的流量就是F[u]-min(D[v],w(u,v)),所以拿它再跟w(u,v)取min

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define R register int
using namespace std;
const int N=200010;
struct edge{
    int v,nxt,w;
}e[N<<1];
int t,n,ans,cnt;
int d[N],f[N],fir[N],deg[N];
bool vis[N];

inline void add(int u,int v,int w) {e[++cnt].v=v,e[cnt].w=w,e[cnt].nxt=fir[u],fir[u]=cnt;}

inline int g()
{
    R ret=0,fix=1; register char ch;
    while(!isdigit(ch=getchar())) fix=ch==‘-‘?-1:fix;
    do ret=(ret<<3)+(ret<<1)+(ch^48); while(isdigit(ch=getchar()));
    return ret*fix;
}

void dp(int u)
{
    vis[u]=true,d[u]=0;
    for(R i=fir[u];i;i=e[i].nxt)
    {
        R v=e[i].v; if(vis[v]) continue;
        dp(v);
        if(deg[v]==1) d[u]+=e[i].w;
        else d[u]+=min(d[v],e[i].w);
    }
}

void dfs(int u)
{
    vis[u]=true;
    for(R i=fir[u];i;i=e[i].nxt)
    {
        R v=e[i].v; if(vis[v]) continue;
        if(deg[u]==1) f[v]=d[v]+e[i].w;
        else f[v]=d[v]+min(f[u]-min(d[v],e[i].w),e[i].w);
        dfs(v);
    }
}

int main()
{
    t=g();
    while(t--)
    {
        memset(vis,false,sizeof(vis));
        memset(fir,0,sizeof(fir));
        memset(deg,0,sizeof(deg)); ans=0,cnt=0;
        n=g();
        if(n==0||n==1) {putchar(‘0‘),putchar(‘\n‘);continue;}
        for(R i=1;i<n;i++) {R u=g(),v=g(),w=g(); add(u,v,w),add(v,u,w); deg[u]++,deg[v]++;}
        R rt=1;
        dp(rt); f[rt]=d[rt];
        memset(vis,false,sizeof(vis));
        dfs(rt);
        for(R i=1;i<=n;i++) ans=max(ans,f[i]);
        printf("%d\n",ans);
    }
    return 0;
}

(我太菜了。。。。QAQ)

原文地址:https://www.cnblogs.com/Jackpei/p/10420639.html

时间: 2024-11-05 20:45:20

题解 poj3585 Accumulation Degree (树形dp)(二次扫描和换根法)的相关文章

【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

HDU 2196 Computer 二次扫描与换根DP

题意:给定一棵树,求树上所有点到其最远点的距离. 数据范围: 1 <= N <= 100000 ------------------------------------------我是分割线------------------------------------------ 题解:对于每个节点u来说,其可能到达的最长距离为max{其子树内的最长距离,其父节点不经过u的子树内的最长距离}.于是,我们便可以在第一遍dfs中预处理节点x到其子树内的最长距离,顺带求一下次长距离,方便转移. // f[

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 =

[POJ3585]Accumulation Degree

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

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 v

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[

树形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

[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&

0x54 树形DP

树形DP我只知道千万别写森林转二叉树慢的要死 没有上司的舞会 水!裸! #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; struct node { int x,y,next; }a[6100];int len,last[6100]; void ins(int x,int y)