[POJ3585]Accumulation Degree

题面

\(\text{Solution:}\)

有些题目不仅让我们做树型 \(\text{dp}\) ,而且还让我们换每个根分别做一次, 然后这样就愉快的 \(\text{TLE}\) 了,所以我们要用一种方法快速知道所有根的答案。

二次扫描与换根法:

就是先选任意点作根做一遍 \(\text{dp}\) ,求出相关信息,然后再从根往下 \(\text{dfs}\) ,对每一个节点往下走之前进行自顶向下的推导,计算出 "换根" 后的解。

就这题而言就是用父亲的换根后的答案来跟新自己换根前的答案,一般是 换根后父亲的答案+自己换根前的答案-自己对父亲换根后的贡献

#include <set>
#include <cmath>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <assert.h>
#include <algorithm>

using namespace std;

#define fir first
#define sec second
#define pb push_back
#define mp make_pair
#define LL long long
#define INF (0x3f3f3f3f)
#define mem(a, b) memset(a, b, sizeof (a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define Debug(x) cout << #x << " = " << x << endl
#define travle(i, x) for (register int i = head[x]; i; i = nxt[i])
#define For(i, a, b) for (register int (i) = (a); (i) <= (b); ++ (i))
#define Forr(i, a, b) for (register int (i) = (a); (i) >= (b); -- (i))
#define file(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)
#define ____ debug("go\n")

namespace io {
    static char buf[1<<21], *pos = buf, *end = buf;
    inline char getc()
    { return pos == end && (end = (pos = buf) + fread(buf, 1, 1<<21, stdin), pos == end) ? EOF : *pos ++; }
    inline int rint() {
        register int x = 0, f = 1;register char c;
        while (!isdigit(c = getc())) if (c == '-') f = -1;
        while (x = (x << 1) + (x << 3) + (c ^ 48), isdigit(c = getc()));
        return x * f;
    }
    inline LL rLL() {
        register LL x = 0, f = 1; register char c;
        while (!isdigit(c = getc())) if (c == '-') f = -1;
        while (x = (x << 1ll) + (x << 3ll) + (c ^ 48), isdigit(c = getc()));
        return x * f;
    }
    inline void rstr(char *str) {
        while (isspace(*str = getc()));
        while (!isspace(*++str = getc()))
            if (*str == EOF) break;
        *str = '\0';
    }
    template<typename T>
        inline bool chkmin(T &x, T y) { return x > y ? (x = y, 1) : 0; }
    template<typename T>
        inline bool chkmax(T &x, T y) { return x < y ? (x = y, 1) : 0; }
}
using namespace io;

const int N = 2e5 + 2;

int n, T;
int tot, head[N], ver[N<<1], nxt[N<<1], edge[N<<1];
int D[N], F[N], in[N];

void add(int u, int v, int w)
{ ver[++tot] = v, edge[tot] = w, nxt[tot] = head[u], head[u] = tot; }

void DFS(int u, int f)
{
    D[u] = 0;
    for (int i = head[u]; i; i = nxt[i])
        if (ver[i] != f)
        {
            DFS(ver[i], u);
            int v = ver[i];
            if (in[v] == 1) D[u] += edge[i];
            else D[u] += min(D[v], edge[i]);
        }
}

void DP(int u, int f)
{
    for (int i = head[u]; i; i = nxt[i])
        if (ver[i] != f)
        {
            int v = ver[i];
            if (in[u] == 1) F[v] = D[v] + edge[i];
            else F[v] = D[v] + min(F[u] - min(edge[i], D[v]), edge[i]);
            DP(v, u);
        }
}

int main() {

    T = rint();
    while (T --)
    {
        tot = 0; mem(head, 0); mem(in, 0); mem(D, 0); mem(F, 0);

        n = rint();
        for (int i = 1; i < n; ++ i)
        {
            int u = rint(), v = rint(), w = rint();
            add(u, v, w);
            add(v, u, w);
            in[u] ++, in[v] ++;
        }

        DFS(1, 0);
        F[1] = D[1];
        DP(1, 0);
        for (int i = 1; i <= n; ++ i)
            chkmax(F[1], F[i]);
        printf("%d\n", F[1]);
    }
}

原文地址:https://www.cnblogs.com/HNYLMSTea/p/10505895.html

时间: 2024-08-30 12:36:12

[POJ3585]Accumulation Degree的相关文章

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 (树形dp)(二次扫描和换根法)

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

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[

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

【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] =

[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

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

NC201400 树学题解

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

度分布(Degree Distribution)

在图论和网络中,度(degree)是指网络(图)中一个点的与其他点的连接数量,度分布(Degree Distribution)就是整个网络中,各个点的度数量的概率分布. 对于有向图,有入度(in-degree)和出度(out-degree),入度是指指向该节点的边的数量,出度是指从该节点出发指向其他节点的边的数量. 度分布P(k)是指,网络中,度为k的节点的出现概率:对于有向图来说又分为入度分布和出度分布.如果网络中总共有n个节点,其中有nk个节点,他的度为k,那么P(K)=nk/n. 累计度分