#665 (Div. 2)D. Maximum Distributed Tree(贪心)

题目描述

You are given a tree that consists of n nodes. You should label each of its n−1 edges with an integer in such way that satisfies the following conditions:
each integer must be greater than 0;
the product of all n−1 numbers should be equal to k;
the number of 1-s among all n−1 integers must be minimum possible.
Let’s define f(u,v) as the sum of the numbers on the simple path from node u to node v. Also, let ∑i=1n−1∑j=i+1nf(i,j) be a distribution index of the tree.
Find the maximum possible distribution index you can get. Since answer can be too large, print it modulo 109+7.
In this problem, since the number k can be large, the result of the prime factorization of k is given instead.

Input

The first line contains one integer t (1≤t≤100) — the number of test cases.
The first line of each test case contains a single integer n (2≤n≤105) — the number of nodes in the tree.
Each of the next n−1 lines describes an edge: the i-th line contains two integers ui and vi (1≤ui,vi≤n; ui≠vi) — indices of vertices connected by the i-th edge.
Next line contains a single integer m (1≤m≤6⋅104) — the number of prime factors of k.
Next line contains m prime numbers p1,p2,…,pm (2≤pi<6⋅104) such that k=p1⋅p2⋅…⋅pm.
It is guaranteed that the sum of n over all test cases doesn’t exceed 105, the sum of m over all test cases doesn’t exceed 6⋅104, and the given edges for each test cases form a tree.

Output

Print the maximum distribution index you can get. Since answer can be too large, print it modulo 109+7.

Example

input
3
4
1 2
2 3
3 4
2
2 2
4
3 4
1 3
3 2
2
3 2
7
6 1
2 3
4 6
7 3
5 1
3 6
4
7 5 13 3
output
17
18
286

Note

In the first test case, one of the optimal ways is on the following image:
在这里插入图片描述
In this case, f(1,2)=1, f(1,3)=3, f(1,4)=5, f(2,3)=2, f(2,4)=4, f(3,4)=2, so the sum of these 6 numbers is 17.
In the second test case, one of the optimal ways is on the following image:
在这里插入图片描述
In this case, f(1,2)=3, f(1,3)=1, f(1,4)=4, f(2,3)=2, f(2,4)=5, f(3,4)=3, so the sum of these 6 numbers is 18.

题目大意

给你一棵树,你可以在树的边上标权值,使得:
1.每个边权都为正整数
2.这n-1个边权的 乘积 等于 k
3.边权为1的边的数量最少。
f(u,v)为节点u到v的简单路径的权值和。求所有f(u,v)的和的最大值。

题目分析

这道题的难点在于:如何求出每一条边被经过的次数。
我们可以定义sz[i]为以i为根的子树的大小,那么i->fa(i)这条边被经过的次数a[i] 即为:a[i]=sz[i]*(n-sz[i])

只要找出来每一条边被经过的次数,后面只需要简单的贪心即可。
如果所给权值m小于边数cnt,那么就将权值p[i]从大到小给从大到小的a[i],剩下的a[i]的所给权值即为1。
如果所给权值m大于边数cnt,那么就将最大的m-cnt个权值相乘变为一个权值,再将这cnt个权值p[i]从大到小分配给从大到小的a[i]。

代码如下
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <map>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
#include <iomanip>
#define LL long long
#define PII pair<int,int>
using namespace std;
const int N=1e5+5,mod=1e9+7;
vector<LL> g[N];	//存图
int n,m,cnt;
LL p[N],a[N],sz[N];	//p[]所给权值,a[]存所有边被经过的次数,sz[i]以i为根的子树的大小
void init()		//初始化
{
	cnt=0;
	for(int i=0;i<=n;i++)
		g[i].clear();
}
void dfs(int u,int fa)	//dfs求出a[]
{
	sz[u]=1;	//初始大小为1
	for(int i=0;i<g[u].size();i++)
	{
		int v=g[u][i];
		if(v==fa) continue;
		
		dfs(v,u);		//递归
		sz[u]+=sz[v];	//更新u子树的大小
		a[++cnt]=sz[v]*(n-sz[v]);	//将结果存到a[]中
	}
} 
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		init();
		for(int i=1;i<n;i++)		//建图
		{
			int u,v;
			scanf("%d %d",&u,&v);
			g[u].push_back(v);
			g[v].push_back(u);
		}
		scanf("%d",&m);
		for(int i=1;i<=m;i++)
			scanf("%d",&p[i]);
		
		dfs(1,0);		//dfs求a[]
		LL ans=0;
		if(m<n-1)		//m小于边数的情况
		{
			sort(a+1,a+cnt+1,greater<LL>());
			sort(p+1,p+m+1,greater<int>());
			for(int i=1;i<=m;i++)
				ans=(ans+p[i]*a[i]%mod)%mod;
				
			for(int i=m+1;i<=cnt;i++)
				ans=(ans+a[i])%mod;
		}
		else			//m大于边数的情况
		{
			sort(a+1,a+1+cnt);
			sort(p+1,p+1+m);
			LL k=1;
			for(int i=cnt+1;i<=m;i++)
				k=(k*p[i])%mod;
			
			p[cnt]=(p[cnt]*k)%mod;
			for(int i=1;i<=cnt;i++)
				ans=(ans+a[i]*p[i]%mod)%mod;
		}
		printf("%d\n",ans);		//输出ans
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lwz_159

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值