1. 程式人生 > >Codeforces Round #525 (Div. 2) F. Ehab and a weird weight formula

Codeforces Round #525 (Div. 2) F. Ehab and a weird weight formula

F. Ehab and a weird weight formula

題目連結https://codeforces.com/contest/1088/problem/F

題意:

給出一顆點有權值的樹,滿足只有一個點的權值最小,然後除開這個點,每個點都有一個權值比它更小的點與之相鄰。

然後要求你重構這顆樹,滿足點權及邊權和最小。

點權計算方法: au = au*num(num為與之相鄰邊的個數);

邊權計算方法: e{u,v},we = dis(u,v)*min(au,av)  (dis(u,v)為given tree中u和v的距離)。

 

題解:

首先我們若以權值最小的點為根,我們會發現這棵樹越往下,點的權值就會越大。

假定我們隨便選擇一對{u,v},那麼對答案的貢獻就是 au+av+log2(dis(u,v))*min(au,av),要讓權值最小,假設我們先固定u來尋找v,由於受到v的權值和dis(u,v)的限制,所以我們可以考慮採用貪心的思想,對於一個點u,我們儘可能地向其祖先找點v,同時算一下距離。最終會找到一個最優解。

但是直接列舉太慢了,發現首先我們可以優化一下,就是每次往上找2的倍數個結點(因為題目中是log2(dis(u,v)) )。

由於av<au,所以每對{u,v}對答案的貢獻就是au+(log2(dis(u,v))+1)*av。

最終總複雜度是O(nlogn)。

 

程式碼如下:

#include <bits/stdc++.h>
#define INF 999999999999
using namespace std;

typedef long long ll ;
const int N =5e5+5 ;
int a[N],dp[25][N];
int n,st;
vector <int> vec[N];
ll ans;
void dfs(int u,int pa){
    dp[0][u]=pa;
    for(int i=1;i<20;i++){
        if(dp[i-1][u]==-1
) break ; dp[i][u]=dp[i-1][dp[i-1][u]]; } ll minx = INF; int i; for(i=0;i<20;i++){ if(dp[i][u]==-1) break ; minx=min((ll)(i+1)*a[dp[i][u]]+a[u],minx); } minx=min((ll)(i+1)*a[st]+a[u],minx); if(u!=st) ans+=minx; for(auto v:vec[u]){ if(v!=pa) dfs(v,u); } } int main(){ scanf("%d",&n); st=0;a[0]=1e9+5; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); if(a[i]<a[st])st=i; } for(int i=1,u,v;i<n;i++){ scanf("%d%d",&u,&v); vec[u].push_back(v); vec[v].push_back(u); } memset(dp,-1,sizeof(dp)); dfs(st,-1); printf("%I64d",ans); return 0; }