传送门
分析
这道题分析思路很简单——统计每条边的贡献,然后把权值和贡献进行排序,乘一下贪心即可
所以这道题的难点转换成了——我们如何去统计每条边的贡献值
我一开始的思路是用前向星存图,然后每条边进行离散化,最后跑一下bfs,但这个只能统计从一个节点开始往后面走,这条路上面每条边的贡献,如果要完全统计的话就得把每个节点放入队列中,那么时间复杂度就是n平方,明显要tle
后来我们可以发现,这个图很明显是一个树的结构,而在一棵树中,任意两点之间的路径是唯一的,这一点就很有用了
我们如果要统计一条边的贡献,可以先把这条边断掉,然后就形成了两个子树,题目要求每两个点之间都要进行连接,所以这两颗子树中的所有点之间都要进行一次连接,也就是通过断掉的那条边,我们假设其中一颗子树的节点个树是m,那么这条边的贡献就是m * (n - m)
所以我们需要做的就是统计每个节点的子树大小,然后对m * (n - m)进行排序即可
最后需要注意一下权值的数量问题,如果大于n - 1 的话就需要对最大的几个进行合并,如果小的话就需要补1
代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10,mod = 1e9 + 7;
ll w[N];
vector<ll> Q;
int h[N],ne[N * 2],e[N * 2],idx;
int si[N];
int n,m;
void add(int x,int y){
e[idx] = y,ne[idx] = h[x],h[x] = idx++;
}
void init(){
idx = 0;
memset(h,-1,sizeof h);
Q.clear();
Q.push_back(-1);
}
void dfs(int u,int f){
si[u] = 1;
for(int i = h[u];~i;i = ne[i]){
int t = e[i];
if(t == f) continue;
dfs(t,u);
si[u] += si[t];
Q.push_back(1ll * (n - si[t]) * si[t]);
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
init();
scanf("%d",&n);
for(int i = 0;i < n - 1;i++){
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
scanf("%d",&m);
for(int i = 1;i <= m;i++) scanf("%lld",&w[i]);
if(m > n - 1){
sort(w + 1,w + m + 1);
w[n - 1] %= mod;
for(int i = n;i <= m;i++) w[n - 1] = w[n - 1] * w[i] % mod;
}
else{
for(int i = m + 1;i < n;i++) w[i] = 1;
sort(w + 1,w + n);
}
sort(Q.begin(),Q.end());
ll ans = 0;
for(int i = 1;i < n;i++) ans = (ans + w[i] * Q[i]) % mod;
printf("%lld\n",ans);
}
}