P9871 [NOIP2023] 天天爱打卡 题解
题目大意
在 \(n\) 天里,你可以选择一些天进行跑步打卡,不得连续跑步超过 \(k\) 天,每次跑步消耗 \(d\) 的能量值。
此外,有 \(m\) 个任务。对于第 \(i\) 个任务,若在第 \(x_i\) 时,你已经连续打卡了 \(y_i\) 天,就会获得 \(v_i\) 的能量值,问 \(n\) 天以后能量值最高是多少。
其中 \(n\le 10^9\),\(m\le 10^5\)。
容易发现:开始 / 停止跑步的时间节点,一定是任务开始或是结束的时间点。这样,我们对跑步的天数进行离散化,时间复杂度中就不包含 \(n\) 了。
考虑 DP,设 \(f_i\) 表示从第一天开始,到第 \(i\) 个时间节点为止,最多可以获得多少能量。我们可以枚举:到第 \(i\) 个时间节点为止,已经连续跑步了多长时间。容易写出状态转移方程:
第 \(i\) 个时间节点不跑步的情况:
考虑到时间节点相邻的情况(即:在第 \(j\) 天的前一天不能跑步;如果这样,连续跑步的时间可能超过 \(k\)),记 \(g_j\) 表示满足 \(num[k]<num[j]-1\) 的最大的 \(f_k\)。显然,由于 \(f_i\) 单调递增,
上面提到的这种暴力转移的时间复杂度达到了 \(O(n^3)\),需要优化。我们注意到 \([l_p,r_p]\subseteq [j,i]\) 貌似属于一种二维数点问题。我们借用扫描线的思想,将所有区间按 \(r_p\) 排序,每遍历到一个 \(i\),就把所有 \(r_p=i\) 的区间在数据结构的 \(l_p\) 处加上 \(v_p\) 的权值。
这样,时间复杂度就被优化为 \(O(n^2\log n)\),仍需进一步优化。
注意到,状态转移方程可以分解为和 \(i\) 有关的部分(\(-num[i]\times d\))以及和 \(j\) 有关的部分(\(g_j+(nun[j]-1)\times d+\sum_p v_p\))。其中后者的 $ \sum_p v_p$ 不易维护。但我们注意到,每次扫描线处理一个区间 \([l_p,r_p]\) 只对 \(j\le l_p\) 有贡献,可以被刻画为一种区间修改。区修+区查最大值 考虑线段树。
我们使用线段树维护 \(g_j+(num[j]-1)\times d+\sum_p v_p\) 的区间最值:
- 新遍历到一个 \(i\),需要处理 \(\max\{\}\) 中新产生的的一项。我们分讨求出 \(g_i\),并将 \(g_i+(num[i]-1)\times d\) 单点修改到线段树的 \(i\) 位置。
- 每次处理一个区间 \([l_p,r_p]\) 就在线段树上给区间 \([1,l_p]\) 加 \(v_p\)。
- 查询时,先用
lower_bound
找到第一个满足 \(num[j]\ge num[i]-k+1\) 的 \(j\),在线段树上查询 \([j,i]\) 的最大值,并加上 \(-num[i]\times d\) 去更新 \(f_i\)。
技巧
- 可以考虑能贡献到查询操作 的 修改操作 需满足什么条件,或能被修改操作 贡献到的 查询操作 需要满足什么条件。
- 区间的包含关系通常可以被刻画为二维数点问题。
AC 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
|