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

推荐订阅源

Martin Fowler
Martin Fowler
Y
Y Combinator Blog
M
MIT News - Artificial intelligence
The Cloudflare Blog
WordPress大学
WordPress大学
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园 - 司徒正美
小众软件
小众软件
Blog — PlanetScale
Blog — PlanetScale
雷峰网
雷峰网
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
J
Java Code Geeks
云风的 BLOG
云风的 BLOG
C
Check Point Blog
D
DataBreaches.Net
T
The Blog of Author Tim Ferriss
V
V2EX
F
Fortinet All Blogs
B
Blog
大猫的无限游戏
大猫的无限游戏
N
Netflix TechBlog - Medium
B
Blog RSS Feed
A
About on SuperTechFans
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC

博客园_首页

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;
}