题目:[[2024.11.17 题目]]
## 覆盖
在 $[0,w]$ 的坐标轴上有 $n$ 个区间,第 $i$ 个区间为 $[l_i,r_i]$。现在你需要去掉一部分区间,使区间 $(x,x+c)$ 与任意一个原本的区间不重叠。第 $i$ 个区间删去代价为 $p_i$,求满足条件需要的最少代价。
---
首先,我们对区间以 $r$ 为关键字从小至大进行排序,然后枚举 $r_i$,尝试答案区间为 $(r_i,r_i+c)$ 需要的代价。由于 $l_i\le r_i$,因此只有 $j>i$ 的区间 $[l_j,r_j]$ 可能会与 $(r_i,r_i+c)$ 重叠。但是,我们发现这里重叠的标准是 $l$ 是否小于 $r_i+c$,不妨在 $l$ 上面下功夫。
新建另一个区间序列 $b$,将 $b_i$ 赋值为 $[l_i,r_i]$,对 $b$ 以 $l$ 为关键字从小至大排序,此时我们就可以极快地通过二分求出哪一段的 $l_i<r_i+c$ 了。此时我们发现当 $r_i<c$ 时即使 $l_i<r_i+c$ 也不会与我们的答案区间重叠,而原序列又是按照 $r_i$ 进行排序的,因此我们直接新建变量满足 $r_i<c$ 的区间 $p_i$ 记录下来,求答案时需要减去。
最后对于所有的答案区间进行求值即可。时间复杂度 $\mathcal O(n\log_2 n)$。注意 $(0,c)$ 也是一个可选的答案区间,不能忽略,否则会失分。
```cpp
#include <fstream>
#include <algorithm>
using namespace std;
using LL = long long;
const LL kMaxN = 1e5 + 5;
ifstream cin("cover.in");
ofstream cout("cover.out");
struct Interval {
LL l, r, p;
bool operator<(const Interval &that) {
return l < that.l;
}
} a[kMaxN], b[kMaxN];
LL p[kMaxN], n, w, c, sub, ans = 1e18;
LL calc(LL x) {
LL y = lower_bound(b + 1, b + n + 1, Interval{x, x, x}) - b - 1;
return p[y] - sub;
}
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n >> w >> c;
for (LL i = 1; i <= n; i++) {
cin >> a[i].l >> a[i].r >> a[i].p;
b[i] = a[i];
}
sort(a + 1, a + n + 1, [](auto a, auto b) { return a.r < b.r; });
sort(b + 1, b + n + 1);
for (LL i = 1; i <= n; i++) {
p[i] = p[i - 1] + b[i].p;
}
ans = min(ans, calc(c));
for (LL i = 1; i <= n; i++) {
if (a[i].r + c <= w) {
sub += a[i].p;
ans = min(ans, calc(a[i].r + c));
}
}
cout << ans << '\n';
return 0;
}
```
## 分发传单
最开始有 $1$ 个 $1$ 代生物。有 $n$ 秒的时间可以让他们工作,第 $i$ 代生物可以进行如下工作:花费 $1$ 秒分发 $1$ 张传单或花费 $c\times i$ 秒繁殖一个第 $i+1$ 代的生物。多个生物可以同时工作,求 $n$ 秒内可以分发的传单的数量的最大值。
---
题目完全看懂了,但是对于小至 $1\le n_i,c_i\le 20$ 的数据仍然毫无头绪,代码都没输入,文件都没有创建,本题成功获得 $0$ 分。
**后记**:XBC 是机房内唯一一个使用 ${} \mathcal O(n^2)$ dp 的,获得 $50$ 分,本机房最高分。
![[the-end.png#pic_center]]
## 绘画
有 $n$ 个格子,进行 $k$ 次涂色,第 $i$ 将任意连续 $a_i$ 个格子染黑,若已经被染黑则颜色不变。求最后得到不同的结果的数量,答案为 $10^9+7$ 取模。
---
采用最垃圾的手段进行骗分。对于 $k=1$ 的特殊情况,也就是只会涂一次颜色,那么显然不会出现任何一个格子被重复填色的情况,因此答案即为 $n-a_1+1$。成功获得 $10$ 分。
**后记**:HHC 推公式 AC 掉了 $k\le 2$ 的所有测试点,太巨了。
## 三角覆盖
给出一个直角边长为 $n$ 的等腰直角三角形点集,共有 $n$ 行,第 $i$ 行的点为 $(i,1),(i,2),\cdots,(i,i)$。
有 $m$ 次覆盖操作,每次覆盖一个等腰直角三角形区域内的点,第 $i$ 次给出 $3$ 个数 $a_i,b_i,c_i$,表示对于 $1\le k\le j\le c_i$,点 $(a_i+j-1,b_i+k-1)$ 被覆盖了。
随后给出 $q$ 次询问操作,询问一个等腰直角三角形区域内未被覆盖的点数,第 $i$ 次给出 $3$ 个数 $d_i,e_i,f_i$,表示询问所有 $1\le k\le j\le f$,点 $(d_i+j-1,e_i+k-1)$ 有多少未被覆盖。
---
采用差分+前缀和的方式对于 $n\le 5000$ 的数据进行处理。
当 $n$ 较小的时候,我们可以对于每一次处理都枚举每一行,但是不能枚举修改的三角形当中的每一个点。因此不难想出对于每一行进行序列差分。
但是我们需要考虑的很重要一个点,就是我们的差分是对一个区间的每一个元素进行相加,而非将区间内的每一个元素标记为 $1$。这该怎么整呢?
实际上,我们还是正常地进行差分(区间都加 $1$),但是利用前缀和还原之后对于每一个不为 $0$ 的元素其实就是 $1$(原本的值代表它被标记了几次,而只要被标记了正整数次就是被覆盖),否则为 $0$。
然后再进行一次每行的前缀和。这样子我们的查询操作也能做到 $\mathcal O(nq)$。该算法的总时间复杂度为 $\mathcal O(n(m+q))$,成功获得 $30$ 分。