Codeforces Round #800 (Div. 2) D. Fake Plastic Trees

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(0c1...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。用拓扑排序实现遍历即可。

注意两个点:

  1. 如果叶节点的 l [ i ] = 0 l[i]=0 l[i]=0 ,则直接 v a l [ i ] = 0 val[i] = 0 val[i]=0 即可,因为这个点相当于不存在。
  2. 在更新 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值