jzoj3519-灵能矩阵【LCM,树形dp】

版权声明:原创,未经作者允许禁止转载 https://blog.csdn.net/Mr_wuyongcong/article/details/85208305

正题


题目大意

一棵树,每个叶子节点有权值,每个点的权值是它这棵子树中的所有叶子节点权值之和。可以减少叶子节点的值,要求减少最少的值使得对于每个点,它的所有子节点的权值都相等。


解题思路

如果将叶子节点的深度优先访问顺序排好,那么就是一个序列。对于这个序列,我们只可以区间减少。那么我们可以先将一个区间减到满足要求,再考虑更大的区间。
对于每个点我们先不考虑原本权值。我们对于每个点我们构建一种系数 x x
对于第 i i 个点,如果它的子树已经平衡了,那么当我们将总值减去 x i x_i 的倍数时它还是平衡的。
明显这个系数为 l i m x = L C M ( w s o n x ) lim_x=LCM(w_{son_x})
然后这个点的
w x = m i n { w s o n x } s o n _ n u m x + m i n { w s o n x } s o n _ n u m x % l i m x w_x=min\{w_{son_x}\}*son\_num_x+min\{w_{son_x}\}*son\_num_x\% lim_x


c o d e code

#include<cstdio>
#include<vector>
#include<algorithm>
#define ll long long
#define lcm(x,y) x*y/__gcd(x,y)
using namespace std;
const ll N=100010;
vector<int> a[N];
ll n,x,y,w[N],ans,size[N],lim[N];
void dp(ll x)
{
	lim[x]=1;
	if(w[x]) return;
	ll minw=1e18,sum=0;
	for(ll i=0;i<a[x].size();i++)
	{
		ll y=a[x][i];
		dp(y);
		lim[x]=lcm(lim[x],lim[y]);
		minw=min(minw,w[y]);
		sum+=w[y];
	}
	lim[x]*=a[x].size();
	w[x]=minw*a[x].size()-minw*a[x].size()%lim[x];
	ans+=sum-w[x];
}
int main()
{
	//freopen("pylon.in","r",stdin);
	//freopen("pylon.out","w",stdout);
	scanf("%lld",&n);
	for(ll i=1;i<=n;i++)
	  scanf("%lld",&w[i]);
	for(ll i=1;i<n;i++)
	{
		ll x,y;
		scanf("%lld%lld",&x,&y);
		a[x].push_back(y);
	}
	dp(1);
	printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/85208305