题解:P8968 觅光 | Searching for Hope (hard ver.)

题目传送门

简要复述 easy ver. 带给我们的信息(因为这是 hard ver.,所以不提供详细证明):一个关键的结论是我们应当投入相同电荷的小球,这样对方选择的机会最小,显然是对我们有利的。记 \(i\) 的答案为 \(ans_i\),初始 \(ans_i \gets s_i\),然后每一层的另一个节点 \(y\) 带来的贡献有 \(ans_i \gets ans_i+min(ans_i,s_y)\)

所以每次只有两个操作 \(\times 2\) 或加 \(s_y\)。显然 \(\times 2\) 的总操作次数是 \(O(n\log V)\) 次的,可以暴力乘,剩下的打 tag。又有 \(ans_i<sz_y<ans_j\),则 \(2ans_i<ans_j+sz_y\),所以操作后子树内的元素相对顺序是不变的。

具体来说每个节点维护一个小根堆,设一个节点的两个子节点为 \(x,y\)\(s_x>s_y\))。子树 \(y\) 都是 \(\times 2\) 直接暴力,子树 \(x\) 中将 \(>s_y\) 的找出来 \(\times 2\),注意这里不能直接 pop,观察到这些数组成一个包含根的连通块,直接暴力从根往下遍历就好了,时间复杂度 \(O(n\log V + n\log n)\)

Code:

const int N = 1e6 + 10;
int n, rt[N], ls[N], rs[N], dst[N], ch[N][2], fa[N], st[N], nd[N];
long long val[N], laz[N], s[N];
inline void pd(const int &x){
	if(laz[x])
		val[ls[x]] += laz[x], val[rs[x]] += laz[x], laz[ls[x]] += laz[x], laz[rs[x]] += laz[x], laz[x] = 0;
}
inline int merge(int x, int y){
	if(!x || !y)
		return x | y;
	if(val[x] > val[y])
		swap(x, y);
	pd(x);
	rs[x] = merge(rs[x], y);
	if(dst[rs[x]] > dst[ls[x]])
		swap(ls[x], rs[x]);
	dst[x] = dst[rs[x]] + 1;
	return x;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n;
	for(int i = 2; i <= n; i++){
		cin >> fa[i];
		if(ch[fa[i]][0])
			ch[fa[i]][1] = i;
		else
			ch[fa[i]][0] = i;
	}
	for(int i = 1; i <= n; i++)
		cin >> s[i];
	for(int i = n; i; i--){
		if(fa[i])
			s[fa[i]] += s[i];
		val[i] = s[i];
		if(s[ch[i][0]] < s[ch[i][1]])
			swap(ch[i][0], ch[i][1]);
		if(!ch[i][1])
			rt[i] = merge(i, rt[ch[i][0]]);
		else{
			st[st[0] = 1] = rt[ch[i][1]];
			while(st[0]){
				int x = st[st[0]--];
				val[x] *= 2;
				pd(x);
				if(ls[x])
					st[++st[0]] = ls[x];
				if(rs[x])
					st[++st[0]] = rs[x];
			}
			if(rt[ch[i][0]])
				st[st[0] = 1] = rt[ch[i][0]];
			nd[nd[0] = 1] = i;
			while(st[0]){
				int x = st[st[0]--];
				if(val[x] < s[ch[i][1]]){
					nd[++nd[0]] = x, val[x] = (val[x] << 1) - s[ch[i][1]];
					pd(x);
					if(ls[x])
						st[++st[0]] = ls[x];
					if(rs[x])
						st[++st[0]] = rs[x];
				}
			}
			val[rt[ch[i][0]]] += s[ch[i][1]], laz[rt[ch[i][0]]] += s[ch[i][1]];
			rt[i] = merge(merge(rt[ch[i][1]], i), rt[ch[i][0]]);
		}
	}
	st[st[0] = 1] = rt[1];
	while(st[0]){
		int x = st[st[0]--];
		pd(x);
		if(ls[x])
			st[++st[0]] = ls[x];
		if(rs[x])
			st[++st[0]] = rs[x];
	}
	for(int i = 1; i <= n; i++)
		cout << val[i] << " ";
}
posted @ 2025-01-13 11:15  louisliang  阅读(21)  评论(0)    收藏  举报