codeforces 1092 F. Tree with Maximum Cost(树形dp||换根dp)

本文介绍了一种使用换根动态规划方法解决特定树形结构问题的算法。问题的核心是在一棵无根树中找到一个节点u,使得所有节点权值与其到u的距离乘积之和最大。通过一次深度优先搜索预处理各节点子树权值总和,并迭代更新换根后的节点贡献,最终找到最大值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://codeforces.com/contest/1092/problem/F
题意:
给你一棵无根树,每个节点有个权值ai,指定一个点u,定义value=∑ai∗dist(u,i),求出value最大值

思路:换根dp,首先考虑计算以根节点为U计算答案,首先通过一遍dfs直接求出res,然后思考一下如果变换u节点,u->to 那么to节点子树上dis-1,u节点其他子树dis+1,dis-1相当于res减去to节点子树ai的和(因为是每个减一),所以我们在第一遍dfs计算出每个子节点子树ai和num
换根过程:u->to
断开连接:num[u]-=num[to]减去子树大小 ,res-=num[to],减去子树贡献
连接u:num[to]+=num[u],加上u子树大小 ,res+=num[u],把u当做子树加上贡献

子节点遍历完毕,逆序还原即可

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define fi first
#define se second
#define show(a) cout<<a<<endl;
#define show2(a,b) cout<<a<<" "<<b<<endl;
#define show3(a,b,c) cout<<a<<" "<<b<<" "<<c<<endl;
#define max3(a,b,c) max(a,max(b,c))
#define min3(a,b,c) min(a,min(b,c))
using namespace std;
 
typedef long long ll;
typedef pair<char, ll> P;
typedef pair<P, int> LP;
const int inf = 0x3f3f3f3f;
const int N = 1e6 + 100;
const int mod = 1e9+7;
const int base=131;
tr1::unordered_map<ll,ll> mp;
inline ll mul(ll x,ll y) { return (x*y-(ll)((long double)x*y/mod)*mod+mod)%mod;}
inline ll ksm(ll a,ll b) {ll ans=1;while(b){if(b&1)ans=mul(ans,a);a=mul(a,a),b>>=1;}return ans;}
int gcd(int x, int y) {
    return y?gcd(y,x%y):x;
}
ll n,m;
ll num[N],vis[N],a[N];
ll k,ans,cnt,res;
vector<int> v[N];
 
void dfs(int x,int fa,int h)
{
	res+=h*a[x];
	num[x]+=a[x];
	for(int to:v[x])
	{
		if(to==fa) continue;
		dfs(to,x,h+1);
		num[x]+=num[to];
	}
}
void go(int x,int fa)
{
	ans=max(res,ans);
	for(int to:v[x])
	{
		if(to==fa) continue;
		res-=num[to];
		num[x]-=num[to];
		res+=num[x];
		num[to]+=num[x];
		go(to,x);
		num[to]-=num[x];
		res-=num[x];
		num[x]+=num[to];
		res+=num[to];
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
 
 
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<n;i++)
	{
		int x,y;
		cin>>x>>y;
		v[x].push_back(y);
		v[y].push_back(x);
	}
	dfs(1,-1,0);
	go(1,-1);
	cout<<ans<<endl;
 
 
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值