P3365 题解

前言

题目传送门!

更好的阅读体验?

一道个人感觉非常妙的题目,想了半天都想不到,但是听老师一句话就悟了!

思路

本题关键:二叉搜索树,等价于这棵树中序遍历下 \(a_i\) 单调不降

然后这题不就做完了吗。首先跑中序遍历,把一整个 \(a_i\) 求出来。

然后用一个常见的 trick:\(a_i \leftarrow a_i - i\) 就能把单调不降转化为严格单调上升(\(\le\) 变成 \(<\))。

最后根据 P3902 的结论,修改的次数等于 \(n - \operatorname{LIS}(a)\)

所以直接上 \(O(n \log n)\) 求 LIS 的有关算法即可。

代码

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
int val[N], l[N], r[N], a[N], cur;
void dfs(int x) //求中序遍历
{
	if (!x) return;
	dfs(l[x]), cur++, a[cur] = val[x] - cur, dfs(r[x]);
}
int main()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d", &val[i]);
	for (int i = 2; i <= n; i++)
	{
		int fa, ch;
		scanf("%d%d", &fa, &ch);
		if (!fa) continue;
		if (ch == 0) l[fa] = i; else r[fa] = i; //按照题目怪异的读入方式存边
	}
	dfs(1);
	
	vector <int> dp; //n log n 求 LIS
	for (int i = 1; i <= n; i++)
		if (i == 1) dp.push_back(a[i]);
		else
		{
			int pos = lower_bound(dp.begin(), dp.end(), a[i]) - dp.begin();
			if (pos >= (int)dp.size()) dp.push_back(a[i]); else dp[pos] = a[i];
		}
	cout << n - (int)dp.size(); //答案即为 n - LIS
	return 0;
}
posted @ 2023-02-01 17:43  liangbowen  阅读(30)  评论(0编辑  收藏  举报