Codeforces Round #665D Maximum Distributed Tree题解

题目描述:

给你一棵树,你需要将边赋予边权,所有边权的值都要大于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;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值