题目描述:
给你一棵树,你需要将边赋予边权,所有边权的值都要大于0并且它们乘积要是 k k k。求所有点对路径的和最大(具体可以看下样例), k k k按质因数分解的形式给出,当时没过是因为没搞懂这一句话的意思the number of 1-s among all n−1 integers must be minimum possible.这句话应是说要让边权为 1 1 1的边尽量少,就是说如果给出的质数个数小于边数,就只能再给一些边赋边权 1 1 1。
解题思路:
不难想到用贪心将要用到边次数多的赋更大的值,那么我们就需要求出每条要用到的次数,这个可以一个DFS解决,我们以点
1
1
1为根的话,那么除
1
1
1以外的每一个点连父亲的那条边的用到的次数就是(这个点的儿子节点的个数+1)乘(这个点的祖先节点的个数),我们记
(
s
o
n
[
i
]
+
1
)
∗
(
n
−
(
s
o
n
[
i
]
+
1
)
)
(son[i]+1)*(n-(son[i]+1))
(son[i]+1)∗(n−(son[i]+1)),其实也很好理解,我们可以这样想:
s
o
n
[
i
]
+
1
son[i]+1
son[i]+1其实就是这条边"下面"的个数,
n
−
(
s
o
n
[
i
]
+
1
)
n-(son[i]+1)
n−(son[i]+1)就是这条边“上面”点的个数,每个上面的点都会和下面的点产生一条路径,那么根据乘法原理这条边的贡献就是“上面”的点的个数 * “下面”点的个数。
接下来就好做了
1、如果给出的质数个数小于边数,就只能再给一些边赋边权
1
1
1。
2、如果给出的质数个数等于边数,让他们两两匹配。
3、如果给出的质数个数大于边数,那么为了让结果更大,只需要都多出来大的质数都用在贡献最大的边就可以了。
注意:这题要取模,不要有地方漏写了。
代码:
可能写的不是很漂亮
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
const int mod=1e9+7;
vector<long long> v[maxn],num;
long long n;
int dfs(int x,int fa)
{
long long sum=1;
for(int i=0;i<v[x].size();i++)
{
if(v[x][i]==fa)continue;
sum+=dfs(v[x][i],x);
}
if(x!=1)
num.push_back((n-sum)*sum);
return sum;
}
int main(){
int t;
cin>>t;
while(t--)
{
scanf("%lld",&n);
for(int i=0;i<=n;i++)
v[i].clear();
num.clear();
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
int m;
scanf("%d",&m);
vector<int> p;
for(int i=0;i<m;i++)
{
int c;
scanf("%d",&c);
p.push_back(c);
}
dfs(1,-1);
if(m<n-1)
for(int i=0;i<n-1-m;i++)
p.push_back(1);
sort(num.begin(),num.end());
sort(p.begin(),p.end());
long long ans=0;
if(m<=n-1)
{
for(int i=0;i<n-1;i++)
{
ans+=(num[i]*p[i])%mod;
ans%=mod;
}
}
else
{
for(int i=0;i<n-2;i++)
{
ans+=(num[i]*p[i])%mod;
ans%=mod;
}
long long add=1;
for(int i=n-2;i<m;i++)
add=(add*p[i])%mod;
ans+=(add*num[n-2])%mod;
ans%=mod;
}
printf("%lld\n",ans);
}
return 0;
}