jzoj3519-靈能矩陣【LCM,樹形dp】
阿新 • • 發佈:2019-01-05
正題
題目大意
一棵樹,每個葉子節點有權值,每個點的權值是它這棵子樹中的所有葉子節點權值之和。可以減少葉子節點的值,要求減少最少的值使得對於每個點,它的所有子節點的權值都相等。
解題思路
如果將葉子節點的深度優先訪問順序排好,那麼就是一個序列。對於這個序列,我們只可以區間減少。那麼我們可以先將一個區間減到滿足要求,再考慮更大的區間。
對於每個點我們先不考慮原本權值。我們對於每個點我們構建一種係數
。
對於第
個點,如果它的子樹已經平衡了,那麼當我們將總值減去
的倍數時它還是平衡的。
明顯這個係數為
然後這個點的
#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);
}