1. 程式人生 > 實用技巧 >兩兩交換連結串列中的節點

兩兩交換連結串列中的節點

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("");
    }
}