题意
你将获得一棵由无向边连接的树。树上每个节点都有一个魔力值。
我们定义,一条路径的魔力值为路径上所有节点魔力值的乘积除以路径上的节点数。
例如,若一条路径包含两个魔力值分别为 3 , 5 3,5 3,5 的节点,则这条路径的魔力值为 3 × 5 / 2 = 7.5 3\times 5/2=7.5 3×5/2=7.5。
请你计算,这棵树上魔力值最小的路径的魔力值。
输入格式
第一行输入包含整数 n ( 1 ≤ n ≤ 1 0 6 ) n(1≤n≤10^6) n(1≤n≤106)。表示树中的节点数。 接下来 n − 1 n−1 n−1行中的每一行包含两个整数, a i a_i ai 和 b i b_i bi ( 1 ≤ a i , b i ≤ n ) (1≤a_i,b_i≤n) (1≤ai,bi≤n) 表示每条边连接的两个节点的编号。 接下来 n n n 行中的每一行包含一个整数 x i ( 1 ≤ x i ≤ 1 e 9 ) x_i(1≤x_i≤1e9 ) xi(1≤xi≤1e9)。表示第 i i i 个节点的魔力值。
输出格式
以分数 P / Q P/Q P/Q 的形式输出具有最小魔力值的路径的魔力值( P P P 和 Q Q Q 是互质的整数)。 保证 P P P 和 Q Q Q 小于 1 0 18 10^{18} 1018。
样例
样例输入
5
1 2
2 4
1 3
5 2
2
1
1
1
3
样例输出
1/2
分析
显然,我们选的这条链 1 1 1 要越多越好,现在有几种情况。
- 1 1 1 . . . x . . . 1 1 1 ( x ≤ 2 ) 1\ 1\ 1\ ... x...\ 1\ 1\ 1(x \leq 2) 1 1 1 ...x... 1 1 1(x≤2),令 m m m 为长的那一段 1 1 1 的长度, n n n 为短的那一段 1 1 1 的长度,则若我们要选 x x x,要满足 1 / m > 2 / ( m + n + 1 ) 1/m > 2/(m+n+1) 1/m>2/(m+n+1),即 m < n + 1 m < n + 1 m<n+1。
-
1
1
1
.
.
.
x
.
.
.
1
1
1
(
x
>
2
)
1\ 1\ 1\ ... x...\ 1\ 1\ 1(x > 2)
1 1 1 ...x... 1 1 1(x>2),则
1
/
m
>
x
/
(
m
+
n
+
1
)
1/m> x/(m+n+1)
1/m>x/(m+n+1),
经枚举得此方程无解。 - 1 1 1 . . . 2 2... 1 1 1 ( x ≤ 2 ) 1\ 1\ 1\ ... 2\ 2...\ 1\ 1\ 1(x \leq 2) 1 1 1 ...2 2... 1 1 1(x≤2),则 1 / m > 4 / ( m + n + 2 ) 1/m>4/(m+n+2) 1/m>4/(m+n+2),即 3 m < n + 2 3m<n+2 3m<n+2,显然无解。
- 1 1 1 . . . x y . . . 1 1 1 ( x , y > 2 ) 1\ 1\ 1\ ... x\ y...\ 1\ 1\ 1(x,y > 2) 1 1 1 ...x y... 1 1 1(x,y>2),显然无解。
综上,我们发现,这条链只能全是
1
1
1 或 有一个
2
2
2,但是还有一种特殊情况:若树中没有
1
1
1,我们取最小值就行了。
分析一波过后,我们会知道这题肯定能用 树形DP 做。
定义
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0] 为这个结点往下走(即往儿子的方向走)能形成全是
1
1
1 的链能经过的最多的点,
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1] 为这个结点往下走(即往儿子的方向走)能形成有一个
2
2
2 的链能经过的最多的点。
定义
g
[
i
]
[
0
]
g[i][0]
g[i][0] 为经过这个结点 而在它的子树中 能形成全是
1
1
1 的链能经过的最多的点,
g
[
i
]
[
1
]
g[i][1]
g[i][1] 为经过这个结点 而在它的子树中 能形成有一个
2
2
2 的链能经过的最多的点。
(写的有点绕,意会吧。。。)
则
if(a[x] == 1) {
g[x][0] = max(g[x][0], dp[x][0] + dp[Y][0]);
g[x][1] = max(g[x][1], dp[x][0] + dp[Y][1]);
g[x][1] = max(g[x][1], dp[x][1] + dp[Y][0]);
dp[x][0] = max(dp[x][0], dp[Y][0]);
dp[x][1] = max(dp[x][1], dp[Y][1]);
}
if(a[x] == 2) {
g[x][1] = max(g[x][1], dp[x][1] + dp[Y][0]);
dp[x][1] = max(dp[x][1], dp[Y][0]);
}
不想解释了,这就像求树的直径的两个数组。
代码(不用开long long)
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <climits>
#define LL long long
using namespace std;
const int MAXN = 1e6 + 5;
const LL lof = 9e18;
int n, tot, Head[MAXN], Ver[MAXN << 1], Next[MAXN << 1], a[MAXN], minn = 2e9;
LL dp[MAXN][2], g[MAXN][2];
void add(int x, int y) {
Ver[++ tot] = y;
Next[tot] = Head[x]; Head[x] = tot;
}
void read(int &x) {
bool f = 1;
x = 0;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = 0;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
x = f ? x : -x;
}
void dfs(int x, int fa) {
for(int i = Head[x]; i; i = Next[i]) {
int Y = Ver[i];
if(fa == Y) continue;
dfs(Y, x);
if(a[x] == 1) {
g[x][0] = max(g[x][0], dp[x][0] + dp[Y][0]);
g[x][1] = max(g[x][1], dp[x][0] + dp[Y][1]);
g[x][1] = max(g[x][1], dp[x][1] + dp[Y][0]);
dp[x][0] = max(dp[x][0], dp[Y][0]);
dp[x][1] = max(dp[x][1], dp[Y][1]);
}
if(a[x] == 2) {
g[x][1] = max(g[x][1], dp[x][1] + dp[Y][0]);
dp[x][1] = max(dp[x][1], dp[Y][0]);
}
}
if(a[x] == 1) {
dp[x][0] ++; dp[x][1] ++;
g[x][0] ++; g[x][1] ++;
}
if(a[x] == 2) {
dp[x][1] ++; g[x][1] ++;
}
}
int main() {
int x, y;
LL s, t;
read(n);
for(int i = 1; i < n; i ++) {
read(x); read(y);
add(x, y); add(y, x);
}
for(int i = 1; i <= n; i ++) {
read(a[i]);
minn = min(minn, a[i]);
}
s = 1e9; t = 1;
int F = 0;
dfs(1, -1);
for(int i = 1; i <= n; i ++) {
if(g[i][0]) {
F = 1;
if(1.0 * t < s * g[i][0]) {
s = (LL)1; t = g[i][0];
}
}
if(g[i][1]) {
F = 1;
if(2.0 * t < s * g[i][1]) {
s = (LL)2; t = g[i][1];
}
}
}
if(s == 2) {
if(!(t & 1)) {
s >>= 1; t >>= 1;
}
}
if(F) {
printf("%lld/%lld", s, t);
}
else {
printf("%d/%d", minn, 1);
}
return 0;
}