兩兩交換連結串列中的節點
阿新 • • 發佈:2020-08-22
Codeforces Round #665 (Div. 2) D. Maximum Distributed Tree
題意
給定一棵\(n\) 個結點的樹,對這棵樹分配邊權,使得這棵樹的邊權的乘積為\(k\) ,且要求所有兩點的簡單路徑邊權之和最大。
\(k\) 以質因子的形式給出,有\(m\) 個質因子
結果取餘1e9+7
\[n \leq 10^5 ,m \leq 6\cdot 10^4 ,p_i \leq 6\cdot 10^4 \]
分析
顯然我們需要考慮每條邊的貢獻,如何求得一條邊的貢獻次數?簡單排列組合一下就知道,令\(siz(i)\) 為\(i\) 號結點的子樹大小,那麼\(i\) 和父親結點的連邊的貢獻次數就是$ size(i) \cdot (n - siz(i))$ ,這樣我們只要貪心地把最大的質因子分配給貢獻最大的連邊即可,至於為什麼是質因子,可以通過不等式證明。
這裡還需要簡單討論一下 $ m \leq n - 1$ 的情況。
程式碼
ll siz[maxn], p[maxn], q[maxn]; vector<int> e[maxn]; void dfs(int u, int fa) { siz[u] = 1; for (auto it : e[u]) { if (it == fa) continue; dfs(it, u); siz[u] += siz[it]; } } int main() { int T = readint(); while (T--) { int n = readint(); for (int i = 1; i <= n; i++) e[i].clear(), siz[i] = 0, p[i] = 1; for (int i = 1; i < n; i++) { int x, y; scanf("%d%d", &x, &y); e[x].push_back(y); e[y].push_back(x); } dfs(1, 0); int m = readint(); for (int i = 1; i <= m; i++) p[i] = readll(); if (m < n - 1) m = n - 1; sort(p + 1, p + m + 1); for (int i = n; i <= m; i++) { p[n - 1] *= p[i]; p[n - 1] %= MOD; } ll res = 0; for (int i = 1; i < n; i++) q[i] = (n - siz[i + 1]) * siz[i + 1]; sort(q + 1, q + n); for (int i = 1; i < n; i++) res = (res + (p[i] % MOD * q[i] % MOD) % MOD) % MOD; Put(res); puts(""); } }