给定一棵有
N
N
N 个结点的树(树中结点从
1
1
1 到
N
N
N 编号,根结点编号为
1
1
1)。每个结点有一种颜色,或为黑,或为白。
称以结点
u
u
u 为根的子树是 好的,若子树中黑色结点与白色结点的数量之差的绝对值不超过
1
1
1。称整棵树是 完美树,若对于所有
1
≤
i
≤
N
1 \le i \le N
1≤i≤N,以结点
i
i
i 为根的子树都是好的。
你需要将整棵树变成完美树,为此你可以进行以下操作任意次(包括零次):选择任意一个结点
i
i
i (
1
≤
i
≤
N
1 \le i \le N
1≤i≤N),改变结点
i
i
i 的颜色(若结点
i
i
i 目前是黑色则将其改为白色,若结点
i
i
i 目前是白色则将其改为黑色)。这次操作的代价为
P
i
P_i
Pi。
求将给定的树变为完美树的最小代价。
注:以结点
i
i
i 为根的子树,由结点
i
i
i 以及结点
i
i
i 的所有后代结点组成。
输入格式:
输入第一行为一个数
N
N
N (
1
≤
N
≤
1
0
5
1 \le N \le 10^5
1≤N≤105),表示树的结点个数。
接下来的
N
N
N 行,第
i
i
i 行的前三个数为
C
i
,
P
i
,
K
i
C_i,P_i,K_i
Ci,Pi,Ki (
1
≤
P
i
≤
1
0
4
1 \le P_i \le 10^4
1≤Pi≤104,
0
≤
K
i
≤
N
0 \le K_i \le N
0≤Ki≤N),分别表示树上编号为
i
i
i 的结点的初始颜色(
0
0
0 为白色,
1
1
1 为黑色)、变换颜色的代价及孩子结点的数量。紧跟着有
K
i
K_i
Ki 个数,为孩子结点的编号。数字均用一个空格隔开,所有的编号保证在
1
1
1 到
N
N
N 里,且不会有环。
数据中只包含一棵树。
输出格式:
输出一行一个数,表示将树 T 变为完美树的最小代价。
输入样例:
10
1 100 3 2 3 4
0 20 1 7
0 5 2 5 6
0 8 1 10
0 7 0
0 2 0
1 1 2 8 9
0 15 0
0 13 0
1 8 0
输出样例:
15
提示:
样例中最佳的方案是:将 9 9 9 号点和 6 6 6 号点从白色变为黑色,此时代价为 13 + 2 = 15 13 + 2 = 15 13+2=15。
分析
对于一棵树,我们保证其黑白色节点之差
≤
1
\le 1
≤1,我们不妨定义
n
u
m
i
num_i
numi 为颜色
i
i
i 结点个数,
s
i
z
i
siz_i
sizi 为子树结点个数,因此每棵子树都有三个状态:
n
u
m
黑
−
n
u
m
白
=
1
num_黑 - num_白 = 1
num黑−num白=1
n
u
m
白
−
n
u
m
黑
=
1
num_白 - num_黑 = 1
num白−num黑=1
n
u
m
黑
=
n
u
m
白
num_黑 = num_白
num黑=num白
我们定义
f
i
,
j
f_{i,j}
fi,j 为结点
i
i
i 为状态
j
j
j 的最小代价。
我们很容易得出状态转移方程:
令
t
o
t
=
∑
v
(
s
i
z
v
%
2
?
f
i
,
0
:
f
i
,
2
)
tot = \sum_v (siz_v \%\ 2\ ? f_{i,0} :f_{i,2})
tot=∑v(sizv% 2 ?fi,0:fi,2)
当
s
i
z
u
siz_u
sizu 为奇数时:
f
u
,
0
=
t
o
t
−
∑
m
i
n
i
n
2
{
f
v
,
1
−
f
v
,
0
}
f_{u,0} = tot - \sum_{min_i}^{\frac{n}{2}}\{f_{v,1}-f_{v,0}\}
fu,0=tot−∑mini2n{fv,1−fv,0}
f
u
,
1
=
t
o
t
−
∑
m
i
n
i
n
2
+
1
{
f
v
,
1
−
f
v
,
0
}
f_{u,1} = tot - \sum_{min_i}^{\frac{n}{2}+1}\{f_{v,1}-f_{v,0}\}
fu,1=tot−∑mini2n+1{fv,1−fv,0}
当
s
i
z
u
siz_u
sizu 为偶数时:
f
u
,
2
=
t
o
t
−
∑
m
i
n
i
n
2
{
f
v
,
1
−
f
v
,
0
}
f_{u,2} = tot - \sum_{min_i}^{\frac{n}{2}}\{f_{v,1}-f_{v,0}\}
fu,2=tot−∑mini2n{fv,1−fv,0}
代码
void solve()
{
int n;
cin >> n;
vector<int> c(n + 1), p(n + 1);
vector<vector<int>> G(n + 1);
for (int i = 1; i <= n; i++)
{
int m, v;
cin >> c[i] >> p[i] >> m;
for (int j = 1; j <= m; j++)
{
cin >> v;
G[i].push_back(v);
}
}
vector<vector<ll>> f(n + 1, vector<ll>(3, inf));
vector<int> siz(n + 1, 1);
function<void(int)> Dfs = [&](int u)
{
ll tot = 0;
priority_queue<int, vector<int>, greater<int>> q;
for (auto v : G[u])
{
Dfs(v);
siz[u] += siz[v];
if (siz[v] & 1)
{
q.push(f[v][1] - f[v][0]);
tot += f[v][0];
}
else
tot += f[v][2];
}
if (!c[u])
q.push(p[u]);
else
{
tot += p[u];
q.push(-p[u]);
}
int n = q.size();
for (int i = 1; i <= n / 2; i++)
{
tot += q.top();
q.pop();
}
if (siz[u] & 1)
{
f[u][0] = tot;
f[u][1] = tot + q.top();
}
else
f[u][2] = tot;
};
Dfs(1);
cout << min({f[1][0], f[1][1], f[1][2]});
}