abcdeffa's Blog

当局者迷,旁观者清。

0%

GMOJ S1497 【景点中心】

Description

给你一棵有 $n$ 个点的树,点 $i$ 上有 $a_i$ 个人,每条边有一个边权 $c_i$,现在请你求出所有人到同一个点汇合时走的路程总长的最小值,并求出这个点的编号,如果有多个点满足要求,输出距离点 1 最近的那个点的编号即可。

Solution

换根 DP 题中一道比较简单的题目。

设 $f1_i$ 表示点 $i$ 的子树内的答案,$f2_i$ 表示点 $i$ 为根时 $f1_i$ 的值,然后显然可以根据 $f1_i$ 等几个东西来快速地求出 $f2_i$。

于是两遍 DFS 即可解决。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <cstdio>
#define maxN 100010
struct Edge{ long long x, y, c, g; } b[maxN << 1];
long long root = 4, len = 0, Sum = 0;
long long f1[maxN], f2[maxN];
long long sum[maxN], val[maxN], fa[maxN], p[maxN], h[maxN];
void ins (long long x, long long y, long long c)
{
len++;
b[len].x = x;
b[len].y = y;
b[len].c = c;
b[len].g = h[x];
h[x] = len;
}
void dfs1 (long long x)
{
for(long long i = h[x];i;i = b[i].g)
{
long long y = b[i].y;
if(!fa[y])
{
fa[y] = x;
p[y] = b[i].c;
dfs1(y);
sum[x] += sum[y] + val[y];
f1[x] += f1[y] + (sum[y] + val[y]) * b[i].c;
}
}
}
void dfs2 (long long x)
{
if(x != root)
{
long long A = f2[fa[x]];
long long B = f1[x] + (sum[x] + val[x]) * p[x];
long long C = (Sum - sum[x] - val[x]) * p[x];
f2[x] = f1[x] + A - B + C;
}
for(long long i = h[x];i;i = b[i].g)
{
long long y = b[i].y;
if(y != fa[x])
{
dfs2(y);
}
}
}
int main ()
{
long long n = 0;
scanf("%lld", &n);
for(long long i = 1;i <= n; i++)
{
scanf("%lld", &val[i]);
Sum += val[i];
}
for(long long i = 1;i < n; i++)
{
long long x = 0, y = 0, c = 0;
scanf("%lld %lld %lld", &x, &y, &c);
ins(x, y, c);
ins(y, x, c);
}
fa[root] = root;
dfs1(root);
f2[root] = f1[root];
dfs2(root);
long long Ans = 9223372036854775807LL, wz = 0;
for(long long i = 1;i <= n; i++)
{
if(f2[i] < Ans)
{
wz = i;
Ans = f2[i];
}
}
printf("%lld\n%lld", wz, Ans);
return 0;
}