1. 程式人生 > >jzoj3519-靈能矩陣【LCM,樹形dp】

jzoj3519-靈能矩陣【LCM,樹形dp】

正題


題目大意

一棵樹,每個葉子節點有權值,每個點的權值是它這棵子樹中的所有葉子節點權值之和。可以減少葉子節點的值,要求減少最少的值使得對於每個點,它的所有子節點的權值都相等。


解題思路

如果將葉子節點的深度優先訪問順序排好,那麼就是一個序列。對於這個序列,我們只可以區間減少。那麼我們可以先將一個區間減到滿足要求,再考慮更大的區間。
對於每個點我們先不考慮原本權值。我們對於每個點我們構建一種係數 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);
}