1. 程式人生 > 實用技巧 >[luogu p1955] [NOI2015]程式自動分析

[luogu p1955] [NOI2015]程式自動分析

題意:給一棵樹的邊賦邊權,要求邊權積等於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;
}