某题目1 二叉搜索树 最长不下降子序列

Description

给你一棵二叉树,每个点有点权(整数),你可以改变某些点的点权(只能改成整数),使这棵树满足二叉搜索树的性质,求满足条件最少的修改的点的数目。

n <= 1e5

Solution

一看到二叉搜索树,就要做中序遍历,得到序列a

要使修改的点的数量最少,即保留的最多原来的点。

如果能把点权改成小数的话,这个就可以直接做最长上升子序列。

但现在点权只能改成整数,需要考虑空隙的问题。

我们不妨设bi = ai-i,可以发现问题就转化为最长不下降子序列。

为什么呢?

设aj和ai,且满足i > j,若i和j都保留,则必须满足ai-aj >= i-j,整理得ai-i >= aj-j

最长的可保留子序列即ai-i的最长不下降子序列。

Code

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <cstring>
 4 #include <string>
 5 #include <algorithm>
 6 #include <stack>
 7 
 8 using namespace std;
 9 
10 #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_;  ++i)
11 #define mset(a, b) memset(a, b, sizeof(a))
12 const int maxn = 1e5+10;
13 int n, a[maxn], b[maxn], b_cnt;
14 int t[maxn], t_cnt;
15 int ls[maxn], rs[maxn];
16 int mx[maxn*4];
17 
18 void build_tree(int rt, int l, int r)
19 {
20     mx[rt] = 0;
21     if (l == r) return ;
22     int mid = (l+r)>>1;
23     build_tree(rt<<1, l, mid);
24     build_tree((rt<<1)+1, mid+1, r);
25 }
26 
27 void pushup(int rt) { mx[rt] = max(mx[rt<<1], mx[(rt<<1)+1]); }
28 
29 void modify(int rt, int l, int r, int p, int d)
30 {
31     if (l == r) { mx[rt] = max(mx[rt], d); return ; }
32     int mid = (l+r)>>1;
33     if (p <= mid) modify(rt<<1, l, mid, p, d);
34     else modify((rt<<1)+1, mid+1, r, p, d);
35     pushup(rt);
36 }
37 
38 int query(int rt, int l, int r, int L, int R)
39 {
40     if (L <= l && r <= R) return mx[rt];
41     int mid = (l+r)>>1, ret = 0;
42     if (L <= mid) ret = query(rt<<1, l, mid, L, R);
43     if (R > mid) ret = max(ret, query((rt<<1)+1, mid+1, r, L, R));
44     return ret;
45 }
46 
47 int f[maxn];
48 stack <int> s;
49 bool vis[maxn];
50 
51 void bfs(int k)
52 {
53     s.push(k);
54     mset(vis, 0);
55     while (!s.empty())
56     {
57         int u = s.top(); 
58         if (ls[u] != -1 && !vis[ls[u]]) { s.push(ls[u]); continue ; }
59         vis[u] = true, b[++b_cnt] = a[u], b[b_cnt] -= b_cnt; s.pop();
60         if (rs[u] != -1 && !vis[rs[u]]) { s.push(rs[u]); continue ; }
61     }
62 }
63 
64 int main()
65 {
66     scanf("%d", &n);
67     t_cnt = 0;
68     REP(i, 1, n) scanf("%d", &a[i]);
69     mset(ls, -1), mset(rs, -1);
70     REP(i, 2, n)
71     {
72         int fa, ty;
73         scanf("%d %d", &fa, &ty);
74         (ty ? rs[fa] : ls[fa]) = i;
75     }
76     b_cnt = 0, bfs(1);
77     REP(i, 1, n) t[i] = b[i];
78     sort(t+1, t+n+1);
79     t_cnt = 1;
80     REP(i, 2, n)
81         if (t[i] != t[i-1]) t[++t_cnt] = t[i];
82     REP(i, 1, n)
83         b[i] = lower_bound(t+1, t+t_cnt+1, b[i])-t;
84     build_tree(1, 1, t_cnt);
85     REP(i, 1, n)
86     {
87         f[i] = query(1, 1, t_cnt, 1, b[i])+1;
88         modify(1, 1, t_cnt, b[i], f[i]);
89     }
90     printf("%d\n", n-mx[1]);
91     return 0;
92 }
View Code

 

转载于:https://www.cnblogs.com/-ZZB-/p/6635641.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值