P3128 [USACO15DEC] Max Flow P题解(树上差分,最近公共祖先,图论)

前言:

题目链接:P3128 [USACO15DEC] Max Flow P - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

讲解:

这一题含金量真算高的,包含了建树(用了图论的知识),求最近公共祖先(倍增法),还有树上差分(第一次听树上还有差分吧)

这些知识有欠缺的去学习一下上面的几个小知识点吧

思路:

该题我一开始的思路是:虽然一个父亲有多个孩子,但是孩子只有一个父亲,就准备用一个fa数组或者一个map(孩子是键值)来存父子关系,然后有一个book数组,当输入s和t时,就从t加到s,用book标记每个点的经过次数,后面发现这个做法不可行----->原因1:给出的x和y并不确定哪个是父亲 原因2:s和t也不确定哪个是父亲------->思路转变

该题实际要(第四声)求的是:被经过最多次数的点;这个题涉及到的是图或者树,并且更改的是一段中的数据------->频繁更改某个区间------->想到差分----->树差分

(当涉及更改某个区间时,我想到了线段树,但是线段树一般方便查询的是 某个区间 的相关属性(如区间和),但是该题最后并未涉及和区间有关的求值,而是求单点的信息,因此线段树这种做法可以放后面考虑, ++++但是线段树是可以做的++++,也有相关的题解,感兴趣的和想复习线段树的大佬可以去做做)

AC代码:

cpp 复制代码
const int N = 5e4 + 5;
const int M = N - 1;

int cnt;
int head[N];
int fa[N][21];
int deepth[N];
int power[N];
int ans;

struct EDGE
{
	int v;
	int next;
}EDGE[M * 2];

void add(int u, int v)
{
	cnt++;
	EDGE[cnt].v = v;
	EDGE[cnt].next = head[u];
	head[u] = cnt;
}

void dfs(int son, int father)
{
	//第2^0个父亲就是这个父亲
	fa[son][0] = father;
	//儿子深度 = 父亲深度 + 1
	deepth[son] = deepth[father] + 1;
	//算低2 ^ i个父亲是谁
	for (int i = 1; (1 << i)/*注意不是i << 1*/ <= deepth[son]; i++)
		fa[son][i] = fa[fa[son][i - 1]][i -1];//公式
	for (int i = head[son]; i; i = EDGE[i].next)
	{
		int v = EDGE[i].v;
		if (v != father)/**/
			dfs(v, son);
	}
}

int lca(int x, int y)
{
	if (deepth[x] < deepth[y])//要让x在y下面,这样子方便后面统一处理
		swap(x, y);

	//使得x,y位于同一高度
	for (int i = 20; i >= 0; i--)//注意是逆序(原因:1、从上往下找比较快 2、若为顺序,则越往上走,找的父亲跨度越大,不符合要求)
	{
		if (deepth[fa[x][i]] >= deepth[y])
			x = fa[x][i];
	}
	if (x == y)//如果两个点已经重合
		return x;
	//找公共祖先且使得x,y位于公共祖先的下一层
	for (int i = 20; i >= 0; i--)
	{
		if (fa[x][i] != fa[y][i])
		{
			x = fa[x][i];
			y = fa[y][i];
		}
	}
	return fa[x][0];//因为位于公共祖先的下一层,所以他们的父亲就是公共祖先
}

void getans(int son, int father)
{
	for (int i = head[son]; i; i = EDGE[i].next)
	{
		if (EDGE[i].v == father)/**/
			continue;
		
		getans(EDGE[i].v, son);
		power[son] += power[EDGE[i].v];
	}
	ans = max(ans, power[son]);
}

void solve()
{
	int n, k;
	cin >> n >> k;
	int u, v, w;

	//建树
	for (int i = 0; i < n - 1; i++)
	{
		cin >> u >> v;
		add(u, v);
		add(v, u);
	}
	dfs(1, 0);//求第2 ^ n个父亲

	//求公共祖先、树上差分
	for (int i = 0; i < k; i++)
	{
		cin >> u >> v;
		int togetherfather = lca(u, v);
		power[u]++;
		power[v]++;
		power[togetherfather]--;
		power[fa[togetherfather][0]]--;
	}
	getans(1, 0);
	cout << ans << endl;
}

int main()
{
	solve();
	return 0;
}
相关推荐
alphaTao6 分钟前
LeetCode 每日一题 2026/4/20-2026/4/26
算法·leetcode·职场和发展
Robot_Nav1 小时前
TD3 —— 双延迟深度确定性策略梯度算法文献解读
算法·td3·drl
斯维赤1 小时前
每天学习一个小算法:归并排序
学习·算法·排序算法
王老师青少年编程1 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【区间贪心】:区间覆盖(加强版)
c++·算法·贪心·csp·信奥赛·区间贪心·区间覆盖(加强版)
碧海银沙音频科技研究院1 小时前
杰理项目开发大全课程
人工智能·深度学习·算法
风一样的航哥2 小时前
LeetCode 2615 等值距离和:前缀和优化O(n)解法深度解析
数据结构·算法·leetcode
生成论实验室2 小时前
生成态势猜想:一种统一的宇宙动力学语法
人工智能·科技·神经网络·算法·信息与通信
旖-旎2 小时前
深搜(二叉树的所有路径)(6)
c++·算法·leetcode·深度优先·递归
啦啦啦_99992 小时前
3. KNN算法之 常用的距离度量方式(欧式&曼哈顿&切比雪夫&闵式距离)
算法
朝风工作室3 小时前
实时全景拼接|支持任意路数输入,8*8K RTX3050 实测 10ms 内
图像处理·算法·计算机视觉