题解 P2986 [USACO10MAR]伟大的奶牛聚集

题解 P2986 [USACO10MAR]伟大的奶牛聚集

题目链接

很好的一道树形dp的题目,我们观察每一个点i的答案,发现答案 f[i] 由两部分组成:

A1.i所有子树中的犇集中到i点

A2.除了i的子树中的所有犇集中到i的父亲节点,再走到i点

f[i] = A1 + A2

我们发现i的答案和i的孩子有关,也和i的父亲有关。一般这样的问题用两次dfs就可以解决。(由于选谁是根节点都无所谓,以下以1号节点为根)

第一次dfs我们求出每一个点的 f[i], 意思是以i为根节点的子树中的牛集中到i点的“不方便值”。显然我们只要统计i点子树中牛的数量 q[i] 然后递推就可以了。

第二次dfs我们要把 f[i] 变成所有牛(而不是子树中的牛)集中到i点的不方便值。设i的父亲是j,i到j的距离是s。ans[i] 就等于 f[j] + (q[1] - q[i]) * s - q[u] * s。 (多想几遍就想通了,要理解上面A1A2的意思)

下面放代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100005
#define ll long long
using namespace std;
inline int read()
{
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
    while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}
ll ans = 1e15, f[N], c[N], q[N];
//f在两次dfs里含义不一样 c是每个点有多少牛 q是每个点和他的子树总共有多少牛
int tot, head[N], n;

struct edge
{
    int to, next, dis;
    edge() {}
    edge(int x, int y, int z) { to = x, next = y, dis = z; }
}a[2 * N];
//邻接表存图
void add(int from, int to, int dis)
{
    a[++tot] = edge(to, head[from], dis);
    head[from] = tot;
}

void dfs(int x, int fa)
{
    q[x] += c[x];
    for (int i = head[x]; i; i = a[i].next)
    {
        int u = a[i].to, s = a[i].dis;
        if (u != fa)
        {
            dfs(u, x);
            q[x] += q[u];
            f[x] += f[u] + q[u] * s;
        }
    }
}
//第一次dfs 求出A1
void dfs2(int x, int fa)
{
    for (int i = head[x]; i; i = a[i].next)
    {
        int u = a[i].to, s = a[i].dis;
        if (u != fa)
        {
            f[u] = f[x] + (q[1] - q[u]) * s - q[u] * s;
            dfs2(u, x);
        }
    }
}
//第二次dfs 求出每个点的答案
int main()
{
    n = read();
    for (int i = 1; i <= n; i++)
        c[i] = read();
    for (int i = 1; i < n; i++)
    {
        int a1 = read(), a2 = read(), a3 = read();
        add(a1, a2, a3);
        add(a2, a1, a3);
    }
    dfs(1, 1);
    dfs2(1, 1);
    for (int i = 1; i <= n; i++)
        ans = min(ans, f[i]); //在所有点中找到最方便的
    cout << ans << endl;
    return 0;
}

19.08.31

原文地址:https://www.cnblogs.com/YuanqiQHFZ/p/11622372.html

时间: 2024-08-28 18:51:26

题解 P2986 [USACO10MAR]伟大的奶牛聚集的相关文章

洛谷 P2986 [USACO10MAR]伟大的奶牛聚集(树形动规)

题目描述 Bessie is planning the annual Great Cow Gathering for cows all across the country and, of course, she would like to choose the most convenient location for the gathering to take place. Bessie正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会.当然,她会选择最方便的地点来举办这次集会

P2986 [USACO10MAR]伟大的奶牛聚集Great Cow Gat…

题目描述 Bessie is planning the annual Great Cow Gathering for cows all across the country and, of course, she would like to choose the most convenient location for the gathering to take place. Bessie正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会.当然,她会选择最方便的地点来举办这次集会

P2986 [USACO10MAR]伟大的奶牛聚集(思维,dp)

题目描述 Bessie is planning the annual Great Cow Gathering for cows all across the country and, of course, she would like to choose the most convenient location for the gathering to take place. Bessie正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会.当然,她会选择最方便的地点来举办这次集会

P2986 [USACO10MAR]伟大的奶牛聚集

题意: 给一棵 n 个点的边 + 点权树,求带权重? 思路: 其实这题和之前那个 Sta 有点像,我们同样只需要预处理出一个 f[u] 代表以 u 为集合点的方便程度,那么我们就可以O(1)的转移了 假设 v 是 u 的儿子,f[v] = f[u] - (siz[v] * len) + (n - siz[v] ) * len = f[u] + (n - 2 * siz[v] )  * len 这题有一个坑,就是你的INF得开的特别大,不然你就没有 100 了 #include <iostream

[USACO10MAR]伟大的奶牛聚集

[USACO10MAR]伟大的奶牛聚集 Bessie正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会.当然,她会选择最方便的地点来举办这次集会. 每个奶牛居住在 N(1<=N<=100,000) 个农场中的一个,这些农场由N-1条道路连接,并且从任意一个农场都能够到达另外一个农场.道路i连接农场A_i和B_i(1 <= A_i <=N; 1 <= B_i <= N),长度为L_i(1 <= L_i <= 1,000).集会可以在N个农场中的

BZOJ 1827 洛谷 2986 [USACO10MAR]伟大的奶牛聚集Great Cow Gather

[题解] 很容易想到暴力做法,枚举每个点,然后对于每个点O(N)遍历整棵树计算答案.这样整个效率是O(N^2)的,显然不行. 我们考虑如果已知当前某个点的答案,如何快速计算它的儿子的答案. 显然选择它的儿子作为集合点,它的儿子的子树内的奶牛可以少走当前点到儿子节点的距离dis,不在它儿子的子树内的奶牛要多走dis. 那么我们维护每个节点的子树内的奶牛总数(即点权和),就可以快速进行计算了.效率O(N). 1 #include<cstdio> 2 #include<algorithm>

[USACO10MAR] 伟大的奶牛聚集 - 树形dp

每个点有重数,求到所有点距离最小的点 就是魔改的重心了 #include <bits/stdc++.h> using namespace std; #define int long long const int N = 1000005; vector <pair<int,int> > g[N]; int siz[N],f[N],vis[N],sum[N],c[N],n,m,t1,t2,t3,tot; void dfs1(int p) { vis[p]=1; siz[p]

洛谷 P2986 [USACO10MAR]Great Cow Gat…(树形dp+容斥原理)

P2986 [USACO10MAR]伟大的奶牛聚集Great Cow Gat… 题目描述 Bessie is planning the annual Great Cow Gathering for cows all across the country and, of course, she would like to choose the most convenient location for the gathering to take place. Each cow lives in on

【题解】P2916 [USACO08NOV]安慰奶牛Cheering up the Cow-C++

原题传送门 这道题用最小生成树来完成,我选用的是kruskal(克鲁斯卡尔)来完成.这道题目在克鲁斯卡尔模板的基础上,有变动的地方只有2处:1.因为必须从一个点出发,而最小生成树最后会让所有点都连通,所以最优的是从c[i]值最低的点出发,所以最后的total要加上最小的c[i]值.2.因为这道题目的权值很特殊,它包含了2*路的长度(来回走两次)+起点的c[i]+终点的c[i](这个也要花费时间),在读入的时候直接处理就可以了.代码就贴在这,因为考虑可能有些题目会卡输入,用了快读,介意的自己改一下