【*1800树上贪心】CF1401 D

86 篇文章 0 订阅
68 篇文章 1 订阅
文章描述了一道编程竞赛题目,涉及到图论中的树和边的贡献计算。通过深度优先搜索确定树的结构,然后使用贪心策略按边权大小分配质因子,以求得最小的总贡献。代码实现中包括了树的构建、排序以及不同情况的处理策略。
摘要由CSDN通过智能技术生成

Problem - D - Codeforces

题意:

思路:

首先,1尽可能少,那就是让pi尽可能不合并地摊到所有边上,然后计算贡献

按照CF惯用套路,这样的n^2贡献肯定是更换枚举对象,我们去枚举边,按边算贡献

对于一条边的贡献应该为sz[u]*(N-sz[u])*p[i]

然后就是贪心了,大的边权摊到经过次数多的边,小边权到次数小的边

同时需要分类讨论这些给定的质因子个数和边数的大小关系

Code:

#include <bits/stdc++.h>

#define int long long

using namespace std;

const int mxn=1e6+10;
const int mod=1e9+7;

array<int,3> E[mxn];

vector<int> G[mxn];

int N,M;
int u[mxn],v[mxn];
int dep[mxn],sz[mxn],p[mxn],v2[mxn];

void dfs(int u,int fa){
	sz[u]=1;
	dep[u]=dep[fa]+1;
	for(auto v:G[u]){
		if(v==fa) continue;
		dfs(v,u);
		sz[u]+=sz[v];
	}
}
bool cmp(int x,int y){
	return x<y;
}
bool cmp2(array<int,3> x,array<int,3> y){
	return x[0]<y[0];
}
void init(){
	for(int i=0;i<=N;i++){
		G[i].clear();
		dep[i]=sz[i]=0;
	}
}
void solve(){
	cin>>N;
	init();
	for(int i=1;i<=N-1;i++){
		cin>>u[i]>>v[i];
		G[u[i]].push_back(v[i]);
		G[v[i]].push_back(u[i]);
	}
	dfs(1,0);
	for(int i=1;i<=N-1;i++){
		if(dep[u[i]]<dep[v[i]]) swap(u[i],v[i]);
		E[i]={sz[u[i]]*(N-sz[u[i]]),u[i],v[i]};
	}
	sort(E+1,E+1+N-1,cmp2);
	cin>>M;
	for(int i=1;i<=M;i++) cin>>p[i];
	sort(p+1,p+1+M,cmp);
	int ans=0;
	/*if(N-1>=M+1){
		for(int i=M+1;i<=N-1;i++) p[i]=1;
		for(int i=1;i<=N-1;i++){
			ans+=E[i][0]*p[i];
			ans%=mod;
		}
	}else{
		for(int i=1;i<=N-1;i++) ans+=E[i][0]*p[i]%mod,ans%=mod;
		for(int i=N;i<=M;i++) ans+=E[1][0]*p[i]%mod,ans%=mod;
	}
	cout<<ans%mod<<'\n';*/
	N --;
		
		if (M <= N) {
			for (int i = 1; i <= N - M; i ++) v2[i] = 1;
			for (int i = N - M + 1; i <= N; i ++) v2[i] = p[i - (N - M)];
		}
		else {
			v2[N] = 1;
			for (int i = 1; i < N; i ++) v2[i] = p[i];
			for (int i = N; i <= M; i ++) v2[N] = v2[N] * p[i] % mod;
		}
		
		for (int i = 1; i <= N; i ++) ans = (ans + E[i][0] % mod * v2[i] % mod) % mod;
		
		printf("%lld\n", ans);
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int __=1;cin>>__;
	while(__--)solve();return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值