[BZOJ]3727 Final Zadanie 公式推导

针对吉丽提出的关于树形结构的算法题目,介绍了一个通过已知路径总长反推节点人数分布的有效解决方法。该算法首先进行假设并利用深度优先搜索进行节点人数的初步分配,然后调整以匹配真实条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

3727: PA2014 Final Zadanie

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 319   Solved: 138
[ Submit][ Status][ Discuss]

Description

吉丽YY了一道神题,题面是这样的:
“一棵n个点的树,每条边长度为1,第i个结点居住着a[i]个人。假设在i结点举行会议,所有人都从原住址沿着最短路径来到i结点,行走的总路程为b[i]。输出所有b[i]。”
吉丽已经造好了数据,但熊孩子把输入文件中所有a[i]给删掉了。你能帮他恢复吗?

Input

第一行一个整数n(2<=n<=300000)。
接下来n-1行,每行两个整数x,y,表示x和y之间有连边。
接下来一行由空格隔开的n个整数b[i](0<=b[i]<=10^9)。

Output

输出一行由空格隔开的n个整数a[i]。
如果你觉得有多组解就任意输出其中一组。

Sample Input

2
1 2
17 31

Sample Output

31 17

HINT

Source

[ Submit][ Status][ Discuss]


HOME Back

达哥竟然出的原题... 假设一个sum[1]推导出所有的a, 再计算与真实的sum[1]之间的差值就可以了.

好吧有点模糊... 自己写的详解具体在这套的第二题 : Click Here 

班上人看见我这个假设法挺懵逼的...尤其是看见第35行的随便假设.... 大胆猜想, 理性推测.

#include<stdio.h>
#include<cstring>
#define Mercer   register int
#define clear(a) memset(a, 0, sizeof(a))
const int maxn = 300005;
typedef long long dnt;
dnt all, punchh, delta;
int num, n, t, T, fake, rightt;
int h[maxn], a[maxn], b[maxn], sum[maxn], dis[maxn], cnt[maxn];
inline const int read(){
	register int x = 0;
	register char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
	return x;
}
struct edge{ int nxt, v;}e[maxn * 2];
inline void add(int u, int v){
	e[++num].v = v, e[num].nxt = h[u], h[u] = num;
	e[++num].v = u, e[num].nxt = h[v], h[v] = num;
}
void dfss(int u, int fa){
	a[u] = sum[u];
	for(int i = h[u]; i; i = e[i].nxt){
		int v = e[i].v;
		if(v == fa) continue;
		sum[v] = (sum[1] - b[v] + b[u]) / 2;
		dis[v] = dis[u] + 1, dfss(v, u);
		a[u] -= sum[v];
		cnt[u]++;
	}
}
inline void work(){
	for(Mercer i = 1; i <= n; ++i) b[i] = read();
	if((10000000 + b[1] - b[fake]) & 1) sum[1] = 10000001;
	else sum[1] = 10000000;
	dfss(1, 1);
	for(Mercer i = 2; i <= n; ++i) all += (dnt) a[i] * dis[i], punchh += dis[i];
	delta = (b[1] - all) * 2;
	for(Mercer i = 2; i <= n; ++i) rightt += (dnt) dis[i] * (1 - cnt[i]);
	delta /= rightt, sum[1] += delta;
	dfss(1, 1);
}
int main(){
	n = read();
	for(Mercer i = 1; i < n; ++i){
		int u = read(), v = read();
		add(u, v);
		if(u == 1) fake = v;
		if(v == 1) fake = u;
	}
	work();
	for(Mercer i = 1; i < n; ++i) printf("%d ", a[i]);
	printf("%d\n", a[n]);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值