传送门
题目大意:
给一个N个点的树,这棵树必须满足3个条件:1.这棵树的边权的乘积为k ,2.要求所有两点的简单路径边权之和最大,3.使得边权1的数量最小。k 以质因子的形式给出,有m 个质因子
解题思路:
这题打比赛的时候只想到了质数怎么分配的,没有想到边的贡献怎么算,后来看了大佬的blog才知道的,Orz,其实你把一个树找个顶点dfs一遍,设size[i]为该结点子树的大小,那么该点与其父亲结点的边的贡献为size[i]⋅(n−size[i]),这样就将边的贡献求出来了,质数的分布则需要分两种情况,第一种情况是m<=n-1,如果没填满数组,则必须用1替代,第二种情况是m>n-1,这种需要贪心,至于从小贪还是从大贪,多写几种情况就知道了,所以排序后要把[n-1,m]的数相乘,最后再与贡献相乘。(PS:size[]数组一开始不要取模,否则后面排序贪心会错)
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#define pb emplace_back
#define LOCAL
using namespace std;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5+5;
typedef long long ll;
typedef pair<int,int> Pii;
template <typename T>inline void read(T& t){
char c=getchar();t=0;
int f=1;
while(!isdigit(c)){
if(c=='-')f=-1;
c=getchar();
}
while(isdigit(c))t=t*10+c-48,c=getchar();
t=f*t;
}
template <typename T,typename... Args> inline void read(T& t,Args&... args){
read(t);read(args...);
}
vector<int> a[maxn];
ll siz[maxn],p[maxn];
void dfs(int x,int fa){
siz[x]=1;
for(auto it:a[x]){
if(it!=fa){
dfs(it,x);
siz[x]+=siz[it];
}
}
}
int main(){
int t;
read(t);
while(t--){
int n,m;
read(n);
for(int i=1;i<maxn;i++){
siz[i]=0;
a[i].clear();
p[i]=1;
}
for(int i=1;i<=n-1;i++){
int u,v;
read(u,v);
a[u].emplace_back(v);
a[v].emplace_back(u);
}
dfs(1,-1);
read(m);
for(int i=1;i<=m;i++){
read(p[i]);
}
for(int i=1;i<n;i++){
siz[i]=(n-siz[i+1])*siz[i+1];
}
sort(siz+1,siz+n);
if(m<=n-1){
sort(p+1,p+n);
ll ans=0;
for(int i=1;i<n;i++){
ans=(ans+p[i]%mod*siz[i]%mod)%mod;
}
printf("%lld\n",ans);
}else{
sort(p+1,p+1+m);
for(int i=m-1;i>=n-1;i--){
p[i]=p[i]*p[i+1]%mod;
}
ll ans=0;
for(int i=1;i<n;i++){
ans=(ans+p[i]%mod*siz[i]%mod)%mod;
}
printf("%lld\n",ans);
}
}
return 0;
}