link
拓扑排序
题意
给你一棵以1为根的树,你可以进行以下操作若干次:
随意选中一个点,设从根到这个点的简单路径一共有
n
n
n 个点,则你可以任选一个序列
c
1
,
c
2
,
.
.
.
,
c
n
(
0
≤
c
1
≤
.
.
.
≤
c
n
)
c_1, c_2,...,c_n(0\leq c_1\leq ...\leq c_n)
c1,c2,...,cn(0≤c1≤...≤cn) ,从根开始依次将这
n
n
n 个点的权值加上
c
i
c_i
ci。
现给出每个点的
l
i
,
r
i
l_i,r_i
li,ri,问至少多少次操作可以使得所有点的权值都处在
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri] 内。
思路
设
v
a
l
[
i
]
val[i]
val[i] 表示点
i
i
i 可以被加的最大权值,初始均为0.
首先一个显而易见的结论:
a
n
s
ans
ans 最多为
n
n
n 次。因为对每个点都可以前面的点都取零,最后一个点取
r
i
r_i
ri 。
于是我们从叶节点开始考虑:由上面的结论,我们肯定是希望叶节点一次被取掉的,这里我们直接把叶节点的
v
a
l
[
i
]
=
r
[
i
]
val[i] = r[i]
val[i]=r[i] ,并累加一次ans(因为序列是单增的,相比取
l
[
i
]
l[i]
l[i],
r
[
i
]
r[i]
r[i] 显然更优)。然后每个父节点的
v
a
l
[
f
a
]
val[fa]
val[fa] 便是它的子节点的
v
a
l
[
s
o
n
]
val[son ]
val[son] 之和,并根据
v
a
l
[
f
a
]
val[fa]
val[fa] 和
l
[
f
a
]
l[fa]
l[fa] 之间的关系维护
a
n
s
ans
ans。用拓扑排序实现遍历即可。
注意两个点:
- 如果叶节点的 l [ i ] = 0 l[i]=0 l[i]=0 ,则直接 v a l [ i ] = 0 val[i] = 0 val[i]=0 即可,因为这个点相当于不存在。
- 在更新 v a l [ f a ] val[fa] val[fa] 时要注意在已经遍历完所有子节点后,若 v a l [ f a ] < l [ f a ] val[fa] < l[fa] val[fa]<l[fa],则需要一次操作(相当于从1走到 f a fa fa 节点, c i = r [ f a ] − v a l [ f a ] c_i=r[fa]-val[fa] ci=r[fa]−val[fa]),并令 v a l [ f a ] = r [ f a ] val[fa] = r[fa] val[fa]=r[fa],若 v a l [ f a ] > r [ f a ] val[fa] > r[fa] val[fa]>r[fa] ,则要令 v a l [ f a ] = r [ f a ] val[fa] = r[fa] val[fa]=r[fa]。 原因是不能超过上限,这里 v a l [ f a ] val[fa] val[fa] 即便减少了也不会影响已经遍历的节点(即它的子节点),因为序列是单调增的,前面的项变小了不会影响单调性。
代码
int n;
int l[maxn], r[maxn];
int val[maxn];
int pa[maxn], in[maxn];
int ans = 0;
int toposort() {
queue<int> q;
for(int i = 1; i <= n; i++) {
if(!in[i]) {
q.push(i);
if(l[i] > 0) {
val[i] = r[i];
ans++;
}
else {
val[i] = 0;
}
}
}
while(!q.empty()) {
int p = q.front();
q.pop();
int fa = pa[p];
in[fa]--;
val[fa] += val[p];
if(in[fa] == 0) {
q.push(fa);
if(val[fa] < l[fa]) {
val[fa] = r[fa];
ans++;
}
val[fa] = min(val[fa], r[fa]);
}
}
return ans;
}
void solve() {
cin >> n;
ans = 0;
pa[1] = 1;
for(int i = 1; i <= n; i++) {
in[i] = 0;
}
for(int i = 2; i <= n; i++) {
int x;
cin >> x;
in[x]++;
pa[i] = x;
}
for(int i = 1; i <= n; i++) {
cin >> l[i] >> r[i];
val[i] = 0;
}
cout << toposort() << endl;
}