220726 T3 最优化问题 (树状数组)

题目描述

在同学们的努力下, 高匀感受到了 alb 的快乐。

高勺意犹未尽,找来了一个长度为 nn 的序列 a_1,a_2,….,a_na1a2.an 。

她想要删除这个序列中的 kk 个数,然后将剩下的数按下标从小到大排列成一个长度为 n-knk 的序列 b_1,b_2,...,b_{n-k}b1b2...bnk

高勺定义她的快乐度为 bb 序列中满足 b_i=ibi=i 的数量,即 \sum_{i=1}^{n-k} [b_i=i]i=1nk[bi=i] 。

高勺想知道她的快乐度的最大值为多少。

输入格式

第一行两个整数 n,k,nk,表示序列的长度和删掉数的个数。

第二行 nn 个整数 a_iai,表示杰哥的序列。

输出格式

输出一个整数,表示 \sum_{i=1}^{n-k} [b_i=i]i=1nk[bi=i] 的最大值

 

DP暴力的话可以得40~50分。考虑正解:

对于一个数ai+x=i,只有当他前面的数删去x过后才会产生1的贡献,我们将原数列按照数值递增,数值相等时位置递减排序,用c[x]维护删去x个数的最大贡献,加入一个数ax,他要产生贡献的话要删去x-ax个数,查询前缀的最大值并由此转移,我们需要一个单点修改和查询前缀max的数据结构,所以用树状数组。

 1 #include <bits/stdc++.h>
 2 #define N 500005
 3 #define fi first
 4 #define se second
 5 #define pi pair<int, int>
 6 //#define loveGsy
 7 using namespace std;
 8 int a[N], n, k, ans, c[N];
 9 pair<int, int> b[N];
10 void add(int x, int v) {
11     x++;
12     for (; x <= n; x += x & (-x)) c[x] = max(c[x], v);
13 }
14 int query(int x) {
15     x++;
16     int s = 0;
17     for (; x; x -= x & (-x)) s = max(s, c[x]);
18     return s;
19 }
20 void solve(int x) {
21     if (a[x] > x) return ;
22     int res = query(x - a[x]) + 1;//从前缀转移
23     add(x - a[x], res);
24     if (x - a[x] <= k && a[x] <= n - k) ans = max(ans, res);
25 }
26 bool cmp(pi a, pi b) {
27     return (a.fi ^ b.fi) ? a.fi < b.fi : a.se > b.se;
28 }
29 int main() {
30 #ifdef loveGsy
31     freopen("tree.in", "r", stdin);
32     freopen("tree.out", "w", stdout);
33 #endif
34     scanf("%d %d", &n, &k);
35     for (int i = 1; i <= n; i++) {
36         scanf("%d", a + i);
37         b[i] = make_pair(a[i], i);
38     }    
39     sort(b + 1, b + n + 1, cmp);
40     for (int i = 1; i <= n; i++) solve(b[i].second);
41     printf("%d\n", ans);
42     return 0;
43 }

 

posted @ 2022-07-27 11:32  YHXo  阅读(26)  评论(0编辑  收藏  举报