【洛谷】P2986 [USACO10MAR]Great Cow Gathering G【树上DP】【换根DP/找重心】

多亏了队里巨巨让我理解了第二种解法orzorz能遇到佬佬们的我真是太幸运辣!!

P2986 [USACO10MAR]Great Cow Gathering G

第一眼就觉得是找重心,可是发现权值是根据所选点动态变化的,然后懵了一会儿想到了第一种解法。

既然有点权和边权那就分开处理,首先用$siz[u]$处理出每个子树总的$c$值,再假设$1$号节点为所求,先统计一遍总不方便值$f[1]$,再跑一遍$dfs$,很容易发现此时往下$DP$时以$v$点为根的不方便值就应该是$f[v]=f[u]-siz[v]*w+(sum-siz[v])*w$,直接找出最优即可。

#include<bits/stdc++.h>

#define ll long long

const ll oo = 1e55;
const int N = 100005;

using namespace std;

int n;
ll c[N];

struct Node {
    int v, nex; ll w;
    Node(int v = 0, int nex = 0, ll w = 0) :
        v(v), nex(nex), w(w) { }
} Edge[N << 1];

int stot, h[N];
void add(int u, int v, ll w) {
    Edge[++stot] = Node(v, h[u], w);
    h[u] = stot;
}

ll siz[N], d[N], f[N];
void dfs1(int u, int fa) {
    siz[u] = c[u];
    for(int i = h[u]; i; i = Edge[i].nex) {
        int v = Edge[i].v;
        if(v == fa)    continue;
        dfs1(v, u);
        siz[u] += siz[v];
        f[u] += f[v];
        f[u] += siz[v] * Edge[i].w;
    }
}

ll sum;
void dfs2(int u, int fa) {
    for(int i = h[u]; i; i = Edge[i].nex) {
        int v = Edge[i].v;
        if(v == fa)    continue;
        f[v] = f[u] + (sum - siz[v]) * Edge[i].w - siz[v] * Edge[i].w;
        dfs2(v, u);
    }
}

int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) {
        scanf("%d", &c[i]);    sum += c[i];
    }
    for(int i = 1; i < n; i ++) {
        int u, v; ll w;
        scanf("%d%d%lld", &u, &v, &w);
        add(u, v, w);
        add(v, u, w);
    }
    dfs1(1, 0);
    dfs2(1, 0);
    ll ans = oo;
    for(int i = 1; i <= n; i ++)
        ans = min(ans, f[i]);
    printf("%lld", ans);
    return 0;
}

第二种方法找带权树重心然后换根DP。

发现了对于每一条连接$u$和$v$的边,取$u$和取$v$为根对答案贡献的区别就是是用$u$子树的$siz$乘这条边权还是用$v$的$siz$。画画图就发现只有当带权树重心为根时能保证每条边的贡献最小。这个权值就应该是$siz$。

于是跑出重心来直接用每个点到重心距离乘$c$即可。(注意这里不用$siz$,因为只用分别计当前点的贡献即可)

(强迫症又专门写了一遍自己的代码风格)


#include<bits/stdc++.h>

#define ll long long

const ll oo = 1e55;
const int N = 100005;

using namespace std;

int n;
ll c[N];

struct Node {
    int v, nex; ll w;
    Node(int v = 0, int nex = 0, ll w = 0) :
        v(v), nex(nex), w(w) { }
} Edge[N << 1];

int stot, h[N];
void add(int u, int v, ll w) {
    Edge[++stot] = Node(v, h[u], w);
    h[u] = stot;
}

ll d[N], f[N], res = 0x3f3f3f3f, rt;
int siz[N], sum;
void dfs1(int u, int fa) {
    int maxr = 0;
    siz[u] = c[u];
    for(int i = h[u]; i; i = Edge[i].nex) {
        int v = Edge[i].v;
        if(v == fa)    continue;
        dfs1(v, u);
        siz[u] += siz[v];
        maxr = max(maxr, siz[v]);
    }
    maxr = max(maxr, sum - siz[u]);
    if(maxr < res)    res = maxr, rt = u;
}

ll dis[N];
void dfs2(int u, int fa) {
    for(int i = h[u]; i; i = Edge[i].nex) {
        int v = Edge[i].v;
        if(v == fa)    continue;
        dis[v] = dis[u] + Edge[i].w;
        dfs2(v, u);
    }
}

int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) {
        scanf("%d", &c[i]);    sum += c[i];
    }
    for(int i = 1; i < n; i ++) {
        int u, v; ll w;
        scanf("%d%d%lld", &u, &v, &w);
        add(u, v, w);
        add(v, u, w);
    }
    dfs1(1, 0);
    dfs2(rt, 0);
    ll ans = 0;
    for(int i = 1; i <= n; i ++)
        ans += 1ll * c[i] * dis[i];
    printf("%lld", ans);
    return 0;
}

呱呱呱呱呱呱呱呱呱呱呱呱呱呱呱(?)

posted @ 2020-11-08 21:10  Wans_ovo  阅读(123)  评论(0编辑  收藏  举报