Codeforces Round #665 (Div. 2) D. Maximum Distributed Tree

题意:给一棵树的边赋边权,要求边权积等于m(m以质因数分解形式给出),且边上1的个数尽量少,目的最大化两两点对路径和之和

吕老板讲过,树上统计两两点对路径和,考虑每条边的贡献,对于一条边,它贡献的次数就是它两侧子树的大小之积,用dfs求出siz,贡献次数w就是siz*(n-siz)

把w数组从大到小排序,如何安排m?

m的质因数分解形式也从大到小排序,要求1最少,那就优先给每个w排上一个数,由排序不等式(或者直觉),大的配大的

首先,考虑边数比m分解的数多的情况,那就尽量安排大的在前面,后面的补1

再考虑边数比m分解数少的情况,想到两个情况,把从大到小安排后,把剩下的安排到最大;或者从小到大安排,再安排剩下的到最大

考虑贡献次数\(w_1\)\(w_2\)的两条边,安排\(a,b,c\)三个数(顺序默认从大到小),结果就是\(acw_1+bw_2\)\(abw_1+cw_2\),显然第二个大,所以策略是最大的安排到最大的边,直到剩下的正好覆盖剩余的边

呼之欲出

#include<bits/stdc++.h>

using namespace std;

inline int rd(){
	int ret=0,f=1;char c;
	while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
	while(isdigit(c))ret=ret*10+c-'0',c=getchar();
	return ret*f;
}
#define pc putchar
#define space() pc(' ')
#define nextline() pc('\n')
void pot(int x){if(!x)return;pot(x/10);pc('0'+x%10);}
void out(int x){if(!x)pc('0');if(x<0)pc('-'),x=-x;pot(x);}

const int MOD = 1e9+7; 
const int MAXN = 100005;

struct Edge{
	int next,to;	
}e[MAXN<<1];
int head[MAXN],ecnt;
inline void add(int x,int y){
	e[++ecnt].next = head[x];
	e[ecnt].to = y;
	head[x] = ecnt;	
}

typedef long long ll;

int n,m;
ll ps[MAXN],w[MAXN];

int siz[MAXN];
void dfs(int x,int pre){
	siz[x]=1;
	for(int i=head[x];i;i=e[i].next){
		int v=e[i].to;
		if(v==pre) continue;
		dfs(v,x);
		siz[x]+=siz[v];	
	}
}

void solve(){
	memset(siz,0,sizeof(siz));
	memset(head,0,sizeof(head));
	memset(ps,0,sizeof(ps));
	ecnt=0;
	//clear
	n=rd();
	int x,y;
	for(int i=1;i<=n-1;i++){
		x=rd();y=rd();
		add(x,y);add(y,x);
	}
	m=rd();
	for(int i=1;i<=m;i++){
		ps[i]=rd();
	}
	sort(ps+1,ps+1+m);
	reverse(ps+1,ps+1+m);
	dfs(1,-1);
	for(int i=1;i<=ecnt;i+=2){
		int u=e[i].to;
		int v=e[i+1].to;
		ll mn=min(siz[u],siz[v]);
		w[(i+1)>>1]=1ll*mn*(n-mn);
		
//		w[(i+1)>>1]=mn;	
	}
	sort(w+1,w+1+(n-1));
	reverse(w+1,w+1+(n-1));
//	cerr<<"DEBUG:";
//	for(int i=1;i<=n-1;i++) cerr<<w[i]<<" ";
//	cerr<<endl;
	ll ans=0;
	if((n-1)>=m){
		for(int i=1;i<=n-1;i++){
			int y=ps[i];
			if(y==0) y=1;
			ans+=(ll)(1ll*w[i]*y);
			ans%=MOD;
		}
//		cerr<<"ANS:";
		cout<<ans<<endl;
		return;
	}else{
		ll tmp=1;
		for(int i=1;i<=(m-n+2);i++){
			tmp*=(ll)(1ll*ps[i]);
			tmp%=MOD;
		}
		ans+=(ll)(1ll*tmp*w[1]);
		ans%=MOD;
		for(int i=2;i<=n-1;i++){
			int j=i+m-n+1;
			ans+=(ll)(1ll*w[i]*ps[j]);
			ans%=MOD;	
		}
//		cerr<<"ANS:";
		cout<<ans<<endl;
		return;
	}
	
}

int main(){
	int t=rd();
	while(t--) solve();
	return 0;
}

posted @ 2020-08-23 15:48  GhostCai  阅读(132)  评论(0编辑  收藏  举报