树形/换根dp题单(更新ing)

文章介绍了在算法竞赛中如何通过BFS(广度优先搜索)和DFS(深度优先搜索)以及动态规划方法计算树的直径,包括F-MinimumMaximumDistance和F-GardeningFriends问题,涉及到了树的节点关系、深度计算以及换根操作的转移方程。
摘要由CSDN通过智能技术生成

树的直径

结论:树上的点到树的直径的两个端点的距离的最大值即为该点到树中所有点的最大值。

F - Minimum Maximum Distance(有标记的直径)

Codeforces Round 903 (Div. 3)A~F_hjing小靖的博客-CSDN博客

在标记点的树的直径,同理可根据树的直径性质证明。

F - Gardening Friends(直径减去换根距离)

#include<bits/stdc++.h>
#define double long double
#define int long long
using namespace std;
const int N = 1e6;
void solve() 
{
    int n, k, c; cin >> n >> k >> c;
    vector<vector<int>>adj(n + 1);
	for(int i = 0; i < n - 1; i ++)
    {
    	int u, v; cin >> u >> v;
    	adj[u].push_back(v);
    	adj[v].push_back(u);
	}
	vector<int>dis(n + 1);
	auto bfs = [&](int x)
	{
		dis.assign(n + 1, -1);
		queue<int>q;
		q.push(x);
		dis[x] = 0;
		while(q.size())
		{
			int u = q.front();
			q.pop();
			for(auto v : adj[u])
			{
				if(dis[v] != -1) continue;
				dis[v] = dis[u] + 1;
				q.push(v);
			}
		}
		int t = -1;
		for(int i = 1; i <= n; i ++) if(t == - 1 || dis[t] < dis[i]) t = i;
		return t;
	};
	
	int a = bfs(1); auto d0 = dis;
	int b = bfs(a); auto da = dis;
	bfs(b); auto db = dis;
	int ans = 0;
	for(int i = 1; i <= n; i ++)
	{
		ans = max(ans, max(da[i], db[i]) * k - d0[i] * c);
	}
	cout << ans << '\n'; 
}
signed main() 
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t; cin >> t;
    while (t--) solve();
}

换根dp

[POI2008] STA-Station - 洛谷(深度之和最大的点)

设u为v的父节点,那么每次把子节点换为根的转移方程,f[v] = f[u] - size[v] + n - size[v]

#include<bits/stdc++.h>
#define double long double
#define int long long
using namespace std;
const int N = 1e6;
void solve() 
{
    int n; cin >> n;
    vector<vector<int>>adj(n + 1);
	for(int i = 0; i < n - 1; i ++)
    {
    	int u, v; cin >> u >> v;
    	adj[u].push_back(v);
    	adj[v].push_back(u);
	}
	vector<int>f(n + 1), size(n + 1), dep(n + 1);
	auto dfs1 = [&](auto dfs1, int u, int fa) -> void
	{
		for(auto v : adj[u])
		{
			if(v == fa) continue;
			dep[v] = dep[u] + 1;
			dfs1(dfs1, v, u);
			size[u] += size[v]; 
		}
		size[u] ++;
	};
	dfs1(dfs1, 1, -1);
	for(int i = 1; i <= n; i ++) f[1] += dep[i];
	auto dfs2 = [&](auto dfs2, int u, int fa) -> void
	{
		
		for(auto v : adj[u])
		{
		    if(v == fa) continue;
			f[v] = f[u] - size[v] + n - size[v];
			dfs2(dfs2, v, u);
		}
	};
	dfs2(dfs2, 1, -1); 
	int t = -1;
	for(int i = 1; i <= n; i ++) if(t == -1 || f[i] > f[t]) t = i;
	cout << t << '\n';
}
signed main() 
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t = 1; //cin >> t;
    while (t--) solve();
}

D - Tree XOR(位运算)

f[v] = f[u] - (a[u] ^ a[v]) * size[v] + (a[u] ^ a[v]) * (n - size[v]);

#include<bits/stdc++.h>
#define double long double
#define int long long
using namespace std;
const int N = 1e6;
void solve() 
{
    int n; cin >> n;
    vector<int>a(n + 1);
    for(int i = 1; i <= n; i ++) cin >> a[i]; 
    vector<vector<int>>adj(n + 1);
	for(int i = 0; i < n - 1; i ++)
    {
    	int u, v; cin >> u >> v;
    	adj[u].push_back(v);
    	adj[v].push_back(u);
	}
	vector<int>f(n + 1), size(n + 1);
	auto dfs1 = [&](auto dfs1, int u, int fa) -> void
	{
		for(auto v : adj[u])
		{
			if(v == fa) continue;
			dfs1(dfs1, v, u);
			size[u] += size[v];
			f[1] += (a[u] ^ a[v]) * size[v];
		}
		size[u] ++;
	};
	dfs1(dfs1, 1, -1);
	auto dfs2 = [&](auto dfs2, int u, int fa) -> void
	{
		
		for(auto v : adj[u])
		{
		    if(v == fa) continue;
			f[v] = f[u] - (a[u] ^ a[v]) * size[v] + (a[u] ^ a[v]) * (n - size[v]);
			dfs2(dfs2, v, u);
		}
	};
	dfs2(dfs2, 1, -1); 
	for(int i = 1; i <= n; i ++) cout << f[i] << " ";
	cout << '\n';
}
signed main() 
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t = 1; cin >> t;
    while (t--) solve();
}

 [USACO10MAR] Great Cow Gathering G - 洛谷

f[v] = f[u] - w * size[v] + w * (cnt - size[v]);

#include<bits/stdc++.h>
#define double long double
#define int long long
using namespace std;
const int N = 1e6;
struct node
{
	int v, w;
};
void solve() 
{
    int n, cnt = 0; cin >> n;
    vector<int>a(n + 1);
    for(int i = 1; i <= n; i ++) cin >> a[i], cnt += a[i]; 
    vector<vector<node>>adj(n + 1);
	for(int i = 0; i < n - 1; i ++)
    {
    	int u, v, w; cin >> u >> v >> w;
    	adj[u].push_back({v, w});
    	adj[v].push_back({u, w});
	}
	vector<int>f(n + 1), size(n + 1);
	auto dfs1 = [&](auto dfs1, int u, int fa) -> void
	{
		for(auto i : adj[u])
		{
			int v = i.v, w = i.w;
			if(v == fa) continue;
			dfs1(dfs1, v, u);
			size[u] += size[v];
			f[1] += w * size[v];
			//cout << v << " " << w << " " << size[v] << '\n';
	    }
	    size[u] += a[u];
	};
	dfs1(dfs1, 1, -1);
	auto dfs2 = [&](auto dfs2, int u, int fa) -> void
	{
		
		for(auto i : adj[u])
		{
			int v = i.v, w = i.w;
		    if(v == fa) continue;
			f[v] = f[u] - w * size[v] + w * (cnt - size[v]);
			dfs2(dfs2, v, u);
		}
	};
	dfs2(dfs2, 1, -1);
	int t = -1; 
	for(int i = 1; i <= n; i ++) 
	{
		//cout << f[i] << '\n';
		if(t == -1 || f[i] < f[t]) t = i;
	}
	cout << f[t] << '\n';
}
signed main() 
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t = 1; //cin >> t;
    while (t--) solve();
}

E - Tree Painting(思维/跟树的最大深度实现一样)

从根往下取是最优的

#include<bits/stdc++.h>
#define double long double
#define int long long
using namespace std;
const int N = 1e6;
struct node
{
	int v, w;
};
void solve() 
{
    int n; cin >> n;
    vector<vector<int>>adj(n + 1);
	for(int i = 0; i < n - 1; i ++)
    {
    	int u, v, w; cin >> u >> v;
    	adj[u].push_back(v);
    	adj[v].push_back(u);
	}
	vector<int>f(n + 1), size(n + 1);
	auto dfs1 = [&](auto dfs1, int u, int fa) -> void
	{
		for(auto v : adj[u])
		{
			//int v = i.v, w = i.w;
			if(v == fa) continue;
			dfs1(dfs1, v, u);
			size[u] += size[v];
	    }
	    size[u] += 1;
	    f[1] += size[u];
	};
	dfs1(dfs1, 1, -1);
	auto dfs2 = [&](auto dfs2, int u, int fa) -> void
	{
		
		for(auto v : adj[u])
		{
			//int v = i.v, w = i.w;
		    if(v == fa) continue;
			f[v] = f[u] - size[v] + (n - size[v]);
			dfs2(dfs2, v, u);
		}
	};
	dfs2(dfs2, 1, -1);
	int t = -1; 
	for(int i = 1; i <= n; i ++) 
	{
		if(t == -1 || f[i] > f[t]) t = i;
	}
	cout << f[t] << '\n';
}
signed main() 
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t = 1; //cin >> t;
    while (t--) solve();
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值