- 题意:(f(i, j)) 代表了从 (i) 到 (j) 的简单路径边权和。要求构造给定树的边权使得 (sum_{i=1}^{n} sum_{j = i}^{n}f(i, j)) 最大。
- 题解:设一条边为 (<u, v>) 然后设 (cnt_u) 为节点 (u) 的子节点的数量,首先自己想到的就是 (dfs) 处理一遍就行。可知每条边的权重对答案贡献为 (cnt_u imes cnt_v imes w_e),然后分配 (m) 个质数给 (w) 就行了。但是很显然如果质数数量小于 (n) 那么就是直接用大的向小的给他们乘。然后如果素数数量大于 (n-1) 那么就考虑如何搞,假设把大的给大的乘,然后,设 (k_1,k_2,k_3) 为素数,且 (k_1 > k_2 > k_3) 并且 (sum_1 > sum_2 > sum_3)。得出不等式:
[k_1 imes k_3 imes sum_1 + k_2 imes sum_2 < k_1 imes k_2 imes sum_1 + k_3 imes sum_2
]
[k_1 imes sum_1 imes(k_3 - k_2) < sum_2 imes(k_2 - k_3)
]
很显然 (k_3 > k_2)等式左边小于零,右边大于零,所以等式成立。
所以以后贪心最好用不等式证明一下正确性。
- 代码:
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 1e6 + 9;
const ll mod = 1e9 + 7;
vector<ll> G[N];
vector<ll> cnt_edge;
vector<ll> tem;
ll cnt[N];
ll n;
void dfs(int u, int fa) {
bool f = 0;
for (auto v : G[u]) {
if (v == fa) continue;
dfs(v, u);
cnt[u] += cnt[v];
}
if (fa != -1)
cnt_edge.push_back(cnt[u] * (n * 1ll - cnt[u]));
}
void solve() {
scanf("%lld", &n);
cnt[n] = 1;
for (int i = 1; i < n; i++) {
ll u, v;
scanf("%lld%lld", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
cnt[i] = 1;
}
dfs(1, -1);
sort(cnt_edge.begin(), cnt_edge.end());
reverse(cnt_edge.begin(), cnt_edge.end());
ll m;
scanf("%lld", &m);
ll ans = 0;
for (ll i = 1; i <= m; i++) {
ll x;
scanf("%lld", &x);
tem.push_back(x);
}
sort(tem.begin(), tem.end());
for (int i = 0; i < tem.size(); i ++) {
tem[i] %= mod;
}
while (tem.size() > cnt_edge.size()) {
ll x = tem[tem.size()-1];
(x *= tem[tem.size() - 2]) %= mod;
tem[tem.size() - 2] = x;
tem.pop_back();
}
reverse(tem.begin(), tem.end());
while (tem.size() < cnt_edge.size()) {
tem.push_back(1);
}
for (ll i = 0; i < cnt_edge.size(); i++) {
(ans += cnt_edge[i] * tem[i] % mod) %= mod;
}
printf("%lld
", ans);
tem.clear();
cnt_edge.clear();
for (ll i = 1; i <= n; i++) G[i].clear();
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
ll n;
scanf("%lld", &n);
while (n--) {
solve();
}
return 0;
}