惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - Franky
N
Netflix TechBlog - Medium
宝玉的分享
宝玉的分享
Google DeepMind News
Google DeepMind News
腾讯CDC
G
Google Developers Blog
Martin Fowler
Martin Fowler
Microsoft Security Blog
Microsoft Security Blog
Recent Announcements
Recent Announcements
爱范儿
爱范儿
Engineering at Meta
Engineering at Meta
Microsoft Azure Blog
Microsoft Azure Blog
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
有赞技术团队
有赞技术团队
Jina AI
Jina AI
人人都是产品经理
人人都是产品经理
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
M
MIT News - Artificial intelligence
罗磊的独立博客
博客园 - 三生石上(FineUI控件)
美团技术团队
WordPress大学
WordPress大学
阮一峰的网络日志
阮一峰的网络日志

博客园_首页

Linux实操--组管理、权限管理和定时任务 Java + EasyExcel 实现单个接口导出多个Excel Mem0 源码解析系列(二):提示词工程的深度剖析 Openclaw TaskFlow究竟是什么?和普通Skill技能有什么区别 博文阅读密码验证 - 博客园 嘉立创开源:应该是全网MicroPython教程最多的开发板 Hermes Agent 集成实践:从协议到生产 2026年AI编程工具横评:Cursor、Codex、Claude Code、Zed、Windsurf Java程序员必看的RAG入门教程 2026 AI效率神器:Superpowers + Claude Code 保姆级教程 本地大模型部署全攻略:从 0 到 1 玩转 Ollama 【从0到1构建一个ClaudeAgent】内存管理-上下文压缩 .NET 高级开发 | 设计、实现一个事件总线框架 电子小白入门之NE555 3. WorkBuddy:隐藏玩法,一键召唤专家,让 AI 以"专家身份"给你干活 和AI一起搞事情#3:Claude Teammate 游戏开发翻车实录 【OpenClaw】通过 Nanobot 源码学习架构---(7)Memory C# .NET 周刊|2026年3月3期 我在 Debian 11 上把 K8s 单机搭起来了,过程没你想的那么顺(/opt 目录版) 深度学习进阶(七)Data-efficient Image Transformer CLI+Skill搭建浏览器AI自动化框架,告别一切重复枯燥任务 告别Token账单无底洞:OpenClaw本地部署,重塑企业数据主权的唯一解 FastAPI+Vue:文件分片上传+秒传+断点续传,这坑我帮你踩平了! SBTI 爆火后,我做了个程序员版的 CBTI。。已开源 + 附开发过程 多模态检索开始进入工程期:用 Sentence Transformers 搭建可落地的 Multimodal RAG 100多行代码实现一个最简单的Agent(用ReAct) Claude Code 通关手册(八):推荐 5 个 Hooks,代码质量提升 3 倍 老板:“有人截图了!”。安全部门:“收到,马上查暗水印!” - why技术 技术之外,皆是人间 C#/.NET/.NET Core技术前沿周刊 | 第 69 期(2026年4.01-4.12)
题解:P14637 [NOIP2025] 树的价值 / tree(官方数据)
xuyifei0302 · 2026-05-03 · via 博客园_首页

首先,我们可以发现对于这棵树整体,一定有一条链自下而上的一条总链,会放弃掉沿途一些链的自身贡献,来壮大这条总链。

所以,这棵树有两种点,一种是守本分,为所在这条链做出本身贡献,不忠于总链的点,另一种是为总链贡献的点。

但是我们发现这并不好统计,因为从上到下具有奇怪的不确定性。所以我们考虑其为上方的链所做的贡献。

一个点的贡献一定为一段区间,起始为这个点本身,终点为汇入总链之中被总链覆盖的位置。

所以,我们设 \(dp_{i,j,0/1}\),表示第 \(i\) 个点,会做出 \(j\) 的贡献,零表示不是总链上的点,一表示是总链上的点,总的表示以 \(i\) 为根的子树的最大值。

那么,对于一个点,其儿子最多有一个点是总链上的。所以可以对其进行转移更新,那么 \(dp_{i,j,1} = \max(dp_{i,j,1}, dp_{son,j+1,1} + dp_{i,j,0} - dp_{son,j,0})\)

我们可以先假定全不是总链上的点,在计算 \(i\) 的时候,将一个不是总链上的点变为总链上的点,取其最大值。

那么,就可以将其所带来的子树影响用 DFS 序的方式压在序列上,因为一个子树上的点在 DFS 序上同属一个区间,即连在一起的,所以可以对每一层开树状数组,来区间加和。

最终答案即为 \(dp_{1, 1, 1}\)

一定要记得清空完!!!

时间复杂度:\(O(nm\log{n})\)

下面是代码:

#include<bits/stdc++.h>
using namespace std;
int t, n, m, f[8005], deep[8005], dp[8005][805][2], in[8005], ou[8005], cnt, wrd[8005], tree[8005][805];
vector<int> v[8005];
int lowerbit(int x) {
	return x & (-x);
}
void change(int x, int y, int id) {
	for (; x <= n; x += lowerbit(x)) {
		tree[x][id] += y;
	}
}
int getsum(int x, int id) {
	int res = 0;
	for (; x; x -= lowerbit(x)) {
		res += tree[x][id];
	}
	return res;
}
void dfs(int u) {
	deep[u] = deep[f[u]] + 1;
	in[u] = ++cnt;
	wrd[cnt] = u;
	for (auto i : v[u]) {
		dfs(i);
	}
	ou[u] = cnt;
	for (int i = 1; i <= deep[u]; i ++) {
		dp[u][i][0] = i;
		dp[u][i][1] = i;
		for (auto j : v[u]) {
			dp[u][i][0] += dp[j][i][0];
		}
		// cerr << dp[u][i][0] << " " << u << " " << i << " $\n";
		for (auto j : v[u]) {
			dp[u][i][1] = max(dp[u][i][1], dp[j][i + 1][1] + dp[u][i][0] - dp[j][i][0]);
			// cerr << dp[u][i][1] << " " << u << " " << i << "\n";
			change(in[j], dp[u][i][0] - dp[j][i][0], i);
			change(ou[j] + 1, dp[j][i][0] - dp[u][i][0], i);
		}
	}
	for (int i = in[u] + 1; i <= ou[u]; i ++) {
		dp[u][deep[wrd[i]] - deep[u]][0] = max(dp[u][deep[wrd[i]] - deep[u]][0], getsum(i, deep[wrd[i]] - deep[u]) + dp[wrd[i]][deep[wrd[i]] - deep[u] + 1][1]);
		// cerr << dp[u][deep[wrd[i]] - deep[u]][0] << " " << deep[wrd[i]] << " " <<  u << "\n";
	}
}
signed main() {
	// freopen("tree1.in", "r", stdin);
	// freopen("tree.out", "w", stdout);
	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> t;
	while (t --) {
		cin >> n >> m;
		for (int i = 2; i <= n; i ++) {
			cin >> f[i];
			v[f[i]].push_back(i);
		}
		dfs(1);
		cout << dp[1][1][1] << "\n";
		for (int i = 1; i <= n; i ++) {
			v[i].clear();
		}
		memset(tree, 0, sizeof(tree));
		memset(dp, 0, sizeof(dp));
		cnt = 0;
	}
	return 0;
}