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