「BZOJ3727 PA2014」Final Zadanie 思维

题面

在这里插入图片描述

分析

不妨看看, b [ f a [ x ] ] − b [ x ] b[fa[x]]-b[x] b[fa[x]]b[x] 等于什么,手算一下,发现是 x x x 子树的总人数 - 不为 x x x 子树成员的总人数。(这个性质还是很容易发现的。)

然后我想的是(如果你只想看正解,后四排可跳过。)

c [ x ] = b [ f a [ x ] ] − b [ x ] c[x]=b[fa[x]]-b[x] c[x]=b[fa[x]]b[x]。令 x x x 为现在的结点, s o n son son 为其儿子, u p up up 为它上面的一堆元素。

∑ c [ s o n ] = − ( s i z e s o n − 1 ) × s u m s o n − s i z s o n × ( x + u p ) \sum c[son]=-(sizeson-1)\times sumson-sizson\times (x+up) c[son]=(sizeson1)×sumsonsizson×(x+up)

c [ x ] = s u m s o n + x − u p c[x]=sumson+x-up c[x]=sumson+xup

你会发现,做到这里,有三个变量,两个方程,做不了。

考虑算出所有结点的人数和。

s u m sum sum 为所有节点的人数和, z s [ x ] zs[x] zs[x] 为子树的人数和。

好,又看到这个式子: b [ f a [ x ] ] − b [ x ] b[fa[x]]-b[x] b[fa[x]]b[x] = x x x 子树的总人数 - 不为 x x x 子树成员的总人数。

我们不妨换个写法,用带有 s u m sum sum 的式子表达。

b [ f a [ x ] ] − b [ x ] = 2 z s [ x ] − s u m b[fa[x]]-b[x]=2zs[x]-sum b[fa[x]]b[x]=2zs[x]sum

所以 ∑ b [ f a [ x ] ] − b [ x ] = ∑ 2 z s [ x ] − ( n − 1 ) × s u m \sum {b[fa[x]]-b[x]}=\sum 2zs[x]-(n-1)\times sum b[fa[x]]b[x]=2zs[x](n1)×sum

ps:这里是核心,也是难点,妙的在于它巧妙地利用了原本的条件。

考虑把 z s [ x ] zs[x] zs[x] 约去,这里有个很巧妙的处理,我也是看了题解才明白的。。。(我太菜了)

对于一个树上的一个节点 x x x,它对 ∑ z s [ i ] \sum zs[i] zs[i] 的贡献为它到 1 1 1 的距离,则 ∑ z s [ i ] \sum zs[i] zs[i] 刚好为 b [ 1 ] b[1] b[1]

所以 ∑ b [ f a [ x ] ] − b [ x ] = 2 × b [ 1 ] − ( n − 1 ) × s u m      ( x ! = 1 ) \sum {b[fa[x]]-b[x]}=2\times b[1]-(n-1)\times sum\ \ \ \ (x!=1) b[fa[x]]b[x]=2×b[1](n1)×sum    (x!=1)

这样就可以算出 s u m sum sum

算出这个后,我们就可以知道 x x x 子树的总人数 + 不为 x x x 子树成员的总人数,与 b b b 数组联立,即可算出子树和。

a n s [ x ] = z s [ x ] − ∑ z s [ s o n [ x ] ]      ( x ! = 1 ) ans[x]=zs[x]-\sum zs[son[x]]\ \ \ \ (x!=1) ans[x]=zs[x]zs[son[x]]    (x!=1)
a n s [ 1 ] = s u m − ∑ a n s [ i ]      ( i ! = 1 ) ans[1]=sum-\sum ans[i]\ \ \ \ (i!=1) ans[1]=sumans[i]    (i!=1)

Code

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#define LL long long
using namespace std;
const int MAXN = 3e5 + 5;
int n, b[MAXN], a[MAXN];
LL sum_, ans[MAXN], zs[MAXN]; 
vector <int> v[MAXN];
void dfs(int x, int fa) {
	if(~fa) sum_ += b[fa] - b[x];
	for(unsigned int i = 0; i < v[x].size(); i ++) {
		int Y = v[x][i]; if(Y == fa) continue; dfs(Y, x);
	}
}
int dfs1(int x, int fa) {
	if(~fa) zs[x] = (b[fa] - b[x] + sum_) / 2;
	LL tmp = 0;
	for(unsigned int i = 0; i < v[x].size(); i ++) {
		int Y = v[x][i]; if(Y == fa) continue;
		tmp += dfs1(Y, x);
	}
	ans[x] = zs[x] - tmp;
	return zs[x];
}
int main() {
	int x, y;
	scanf("%d", &n);
	for(int i = 1; i < n; i ++) scanf("%d%d", &x, &y), v[x].push_back(y), v[y].push_back(x);
	for(int i = 1; i <= n; i ++) scanf("%d", &b[i]);
	dfs(1, -1); sum_ = (b[1] * 2 - sum_) / (n - 1); dfs1(1, -1); ans[1] = sum_;
	for(int i = 2; i <= n; i ++) ans[1] -= ans[i];
	for(int i = 1; i <= n; i ++) printf("%lld ", ans[i]);
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值