---
# System prepended metadata

title: Codeforce 1401 D. Maximum Distributed Tree 解析(思維、DFS、組合、貪心、DP)

---

---
title: Codeforce 1401 D. Maximum Distributed Tree 解析(思維、DFS、組合、貪心、DP)
description: "Codeforce 1401 D. Maximum Distributed Tree 解析(思維、DFS、組合、貪心、DP)"
disqus: hackmd
---
<meta name="robots" content="Codeforce 1401 D. Maximum Distributed Tree 解析(思維、DFS、組合、貪心、DP)">
<!-- toc -->

# Codeforce 1401 D. Maximum Distributed Tree 解析(思維、DFS、組合、貪心、DP)
今天我們來看看CF1401D
[題目連結](https://codeforces.com/problemset/problem/1401/D)

> **題目**
直接看原題比較清楚，略。

### 前言
這次比賽被第C.題搞到剩20分鐘可以寫D.這題，比賽時沒寫出來，比完了以後花了一個多小時Debug才De出來Orz。

### 想法
要注意到，題目中的***distribution index***實際上就是把每條路對於每個點對互相拜訪時，會被經過幾次，乘個權重，全部加起來。例如說$(u,v)$這條邊，$u$那一端接了一個節點個數為$cnt_u$的子樹，$v$那一端接了一個節點個數為$cnt_v$的子樹(code中我是用$dp[.]$來儲存)，那麼總共有$cnt_u\times cnt_v$個點對，在互相拜訪時會經過$(u,v)$這條邊，而***distribution index***就是把所有邊的拜訪次數乘以你分配的權重，然後全部加起來。
而對於每條邊，如果要找到兩個節點分別連接到多大的子樹(注意，這邊所說的子樹代表由當前點為根，且不會經過目前考慮的邊的子樹)，我們可以隨便選一個點$r$為根，做樹上DP維護每個點為根，往下的子樹(也就是以$r$為根來說，我們考慮的子樹不會往$r$接近)的節點個數。接著就是一些簡單的實作了。
要注意以下幾點:
1. 對於某個邊$(u,v)$，這個邊會被經過的次數為:$dp[v]\times(n-dp[v])$ (假設$u$比$v$淺，需要先在dfs時維護每個點的depth)
2. 最後把權重分配到邊上時，首先當然兩個數列都要先排序，接著要記得考慮$n-1>m$和$n-1\le m$的情況(我就是在這裡卡了一個多小時)

### 程式碼:
```cpp=
int _n=1e5+10;
int t,n,m,p[_n],x,y,dp[_n],dep[_n];
VI G[_n];
PII e[_n];
ll ecnt[_n];
void dfs(int v,int fa,int d){
  dep[v]=d;
  if(SZ(G[v])==1 and G[v][0]==fa){dp[v]=1;return;}
  int res=1;
  rep(i,0,SZ(G[v]))if(fa!=G[v][i])dfs(G[v][i],v,d+1),res+=dp[G[v][i]];
  dp[v]=res;
}
main(void) {ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
  cin>>t;while(t--){
    cin>>n;rep(i,0,n)G[i].clear();
    rep(i,0,n-1){cin>>x>>y;x--,y--;G[x].pb(y),G[y].pb(x);e[i]={x,y};}
    cin>>m;rep(i,0,m)cin>>p[i]; sort(p,p+m);
    dfs(0,-1,0); ll ans=0;rep(i,0,n-1){
      int u=e[i].fi,v=e[i].se;
      if(dep[u]>dep[v])swap(u,v);
      ecnt[i]=1ll*dp[v]*(n-dp[v]);
    } sort(ecnt,ecnt+n-1);
    int len=max(0,n-1-m);
    rep(i,0,len)ans+=ecnt[i],ans%=mod;
    if(n-1>m)rep(i,len,len+m)ans+=1ll*p[i-len]*ecnt[i],ans%=mod;
    if(m>=n-1){
      rep(i,0,n-2)ans+=1ll*p[i]*ecnt[i],ans%=mod;
      ll tmp=1;rep(i,n-2,m)tmp*=1ll*p[i],tmp%=mod;
      ans+=1ll*tmp*(ecnt[n-2]%mod); ans%=mod;
    }
    cout<<ans<<'\n';
  }
  return 0;
}
```
標頭、模板請點Submission看
[Submission](https://codeforces.com/contest/1401/submission/90626023)