§ Description
Link.
定义一条链的价值为链上点权乘积除以节链上点数,求一条价值最小的链。
§ Solution
结论:答案链上最多包含一个 (其余全为 ),并且不在链的两端点。
证明:我们问题分成两个 。
- :。
答案显然为 。
-
:。
-
- 我们设我们选出的链为大概这样的造型:
即一堆 中夹了一个 。
我们设 左边有 个节点,右边有 个节点。
则价值为整条链 ,左边 ,右边 。
为方便我们这里设 。
那么左边的价值一定大于右边。
这里假设 ,则有 ,又 ,所以 。(反之可以证伪,懒得写了 QwQ)
所以有 。
又 ,所以 。
-
- 我们设我们选出的链为大概这样的造型:
即一堆 中夹了一个 一个 。
这里我们可以把 以前当成 的第一个类型,设其共有 个数。
那么假设我们加入 更优,即有 ,则有 ,由于 ,所以加入 是更劣的。
后面的同理就可以推广了。
于是得证 QwQ。
然后我们就可以 DP 了。
设 表示节点 权值为的情况下最优答案。
转移就分类讨论一下:
答案也需要分类讨论(这里设 ):
答案为 ,以及 。
答案为 。
用四个变量维护最大、次大的 即可。
#include <cstdio>
const int MAXN = 1e6 + 5;
int rint () {
int x = 0, f = 1; char c = getchar ();
for ( ; c < '0' || c > '9'; c = getchar () ) f = c == '-' ? -f : f;
for ( ; c >= '0' && c <= '9'; c = getchar () ) x = ( x << 3 ) + ( x << 1 ) + ( c & 15 );
return x * f;
}
template<typename _T>
void wint ( _T x ) {
if ( x < 0 ) putchar ( '-' ), x = ~ x + 1;
if ( x > 9 ) wint ( x / 10 );
putchar ( x % 10 ^ '0' );
}
template<typename _T> _T MIN ( const _T x, const _T y ) { return x > y ? y : x; }
struct starS {
int to, nx;
starS ( int T = 0, int N = 0 ) { to = T, nx = N; }
} as[MAXN * 2];
int n, cnt, Up = 1e9, Dn = 1, mnMg = 1e9, a[MAXN], f[MAXN][2], bgin[MAXN];
void pushEdge ( const int u, const int v ) { as[++ cnt] = starS ( v, bgin[u] ); bgin[u] = cnt; }
void checkUpt ( const int x, const int y ) { if ( Up * y > Dn * x ) Up = x, Dn = y; }
void dfs ( const int u, const int lst ) {
int mx0 = 0, se0 = 0, mx1 = 0, se1 = 0;
for ( int i = bgin[u]; i; i = as[i].nx ) {
int v = as[i].to;
if ( v == lst ) continue;
dfs ( v, u );
if ( f[v][0] > f[mx0][0] ) se0 = mx0, mx0 = v;
else if ( f[v][0] > f[se0][0] ) se0 = v;
if ( f[v][1] > f[mx1][1] ) se1 = mx1, mx1 = v;
else if ( f[v][1] > f[se1][1] ) se1 = v;
}
if ( a[u] == 1 ) {
f[u][0] = f[mx0][0] + 1;
checkUpt ( 1, f[mx0][0] + f[se0][0] + 1 );
if ( ! mx1 ) return;
f[u][1] = f[mx1][1] + 1;
if ( mx0 != mx1 ) checkUpt ( 2, f[mx0][0] + f[mx1][1] + 1 );
else {
checkUpt ( 2, f[se0][0] + f[mx1][1] + 1 );
if ( se1 ) checkUpt ( 2, f[mx0][0] + f[se1][1] + 1 );
}
}
else if ( a[u] == 2 ) f[u][1] = f[mx0][0] + 1, checkUpt ( 2, f[mx0][0] + f[se0][0] + 1 );
}
int main () {
n = rint ();
for ( int i = 1, u, v; i < n; ++ i ) {
u = rint (), v = rint ();
pushEdge ( u, v ), pushEdge ( v, u );
}
for ( int i = 1; i <= n; ++ i ) a[i] = rint (), mnMg = MIN ( mnMg, a[i] );
if ( mnMg > 1 ) wint ( mnMg ), putchar ( '/' ), wint ( 1 ), putchar ( '\n' );
else dfs ( 1, 0 ), wint ( Up ), putchar ( '/' ), wint ( Dn ), putchar ( '\n' );
return 0;
}