洛谷 P 2986 [ U S A C O 10 M A R ] G r e a t C o w G a t h e r i n g G \Huge{洛谷P2986\ [USACO10MAR]\ Great\ Cow\ Gathering\ G} 洛谷P2986 [USACO10MAR] Great Cow Gathering G
本题有一道简单版本,难度属于树形dp的简单题。
题目地址:P3478 [POI2008] STA-Station - 洛谷
题解:[POI2008] STA-Station/洛谷P3478(树形dp)-CSDN博客
题意
有 n n n个农场,每个农场相互可以到达(联通),有 n − 1 n-1 n−1条路连接这 n n n个农场。每条路对应的有其长度 L i L_i Li,然后每个农场有若干头牛 C i C_i Ci。
现在要选择一个农场,并让所有牛都去,求再哪个农场举办能使得所有牛移动距离最小?输出最小的移动距离总和。
数据范围:
- 1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1≤n≤105
- 1 ≤ A i ≤ B i ≤ n 1\leq A_i\leq B_i\leq n 1≤Ai≤Bi≤n
- 0 ≤ C i , L i ≤ 1 0 3 0 \leq C_i,L_i \leq 10^3 0≤Ci,Li≤103。
思路
对于这道题,如果写过上面那道简单题的话,这道题很容易就能写出来。不过这一道也属于树形 d p dp dp中的典中典。
这两道题都属于树形 d p dp dp中的选择节点型。
对于这道题,假设x节点最优,那么x节点的最小移动距离总和=所有子节点的移动距离总和+每个子节点的子节点奶牛个数*这个子节点距离x的距离。
上面的描述可以用下列公式表示为:
b
[
x
]
.
s
e
c
o
n
d
=
∑
b
[
i
]
.
s
e
c
o
n
d
+
b
[
i
]
.
f
i
r
s
t
×
z
b[x].second=\sum{b[i].second+b[i].first\times z}
b[x].second=∑b[i].second+b[i].first×z
以上涉及到的每个节点的子孙节点个数以及移动距离总和,都可以通过一次
d
f
s
dfs
dfs完成。
然后考虑从
x
x
x节点转移到其子节点
i
i
i的状态转移方程:
b
[
i
]
.
s
e
c
o
n
d
=
b
[
x
]
.
s
e
c
o
n
d
+
(
(
b
[
1
]
.
f
i
r
s
t
−
b
[
i
]
.
f
i
r
s
t
)
×
z
)
−
b
[
i
]
.
f
i
r
s
t
×
z
b[i].second = b[x].second + ((b[1].first - b[i].first) \times z) - b[i].first \times z
b[i].second=b[x].second+((b[1].first−b[i].first)×z)−b[i].first×z
第二个dfs中
b
[
1
]
.
f
i
r
s
t
b[1].first
b[1].first相当于所有奶牛的个数。
标程
#define int long long
#define fi first
#define se second
const int N = 1e5 + 10;
int n, res;
vector<PII> a[N];
vector<PII> b(N);
void dfs1(int x, int y) {
for(auto [i, z] : a[x]) {
if(i == y) continue;
dfs1(i, x);
b[x].fi += b[i].fi;
b[x].se += b[i].fi * z + b[i].se;
}
}
void dfs2(int x, int y) {
for(auto [i, z] : a[x]) {
if(i == y) continue;
b[i].se = b[x].se + ((b[1].fi - b[i].fi) * z) - b[i].fi * z;
dfs2(i, x);
}
}
void Solved() {
cin >> n;
for(int i = 1; i <= n; i ++ ) {
cin >> b[i].fi;
}
for(int i = 1; i < n; i ++ ) {
int x, y, z; cin >> x >> y >> z;
a[x].push_back({y, z}); a[y].push_back({x, z});
}
dfs1(1, 1);
dfs2(1, 1);
res = 1e18;
for(int i = 1; i <= n; i ++ ) {
res = min(res, b[i].se);
}
cout << res << endl;
}