CF542B 做题记录
本人对正解给出了另一种理解方法。
- 在开始前,声明一下本文的变量名:把输入的 \(r\) 视为 \(m\),输入的 \(h_i,t_i\) 视为 \(l_i,r_i\)。
首先鸭子往左移动可以看作猎人往右移动。问题转化为:猎人可以在一些位置开枪,相邻的两个位置距离 \(\ge m\),求鸭子数量的最大值。
这是一个非常简单的 dp:设 \(f[i]\) 表示只考虑右端点 \(\le i\) 的所有鸭子,且在位置 \(i\) 打了一枪,打死的鸭子数量的最大值。
从 \(i-1\) 转到 \(i\) 时,首先令 \(f[i]\gets \max\limits_{j=0}^{i-m} f[j]\),然后对于每个 \([l,i]\) 的鸭子,令 \(f[l...i]\) 全部 \(+1\)。
考虑优化这个 dp:我们提取一些有用的关键点,只在关键点上转移。
显然我们可以提取关键点集合 \(S=\{l_{1...n}\}\)。
但是不能得到最优答案。考虑对于一个关键点 \(l_i\),我们添加一个辅助点 \(l_i+m\),可以发现辅助点在转移时可以继承关键点的信息。
但是一个辅助点可能还会附带一个辅助点,考虑剪枝。
对于一个辅助点 \(x\),设上一个关键点/辅助点为 \(y\),若 \(f[x]\le f[y]\),那么 \(x\) 是没用的。
正确性证明:考虑如果 \(f[x]\le f[y]\),那么唯一一种使得后来 \(f[x]>f[y]\) 的方法为:把 \(f[l_i...r_i]\) 一段后缀全部 \(+1\)。
但是由于 \(x\) 是辅助点,不管 \(y\) 是关键点还是辅助点,\(f[x]\) 和 \(f[y]\) 都只会同时不变,或同时 \(+1\),或前者不变后者 \(+1\),所以之后任何时刻 \(f[x]\) 是不可能比 \(f[y]\) 大的。
而且这样做时间复杂度可以得到保证,证明:
考虑一个关键点/辅助点 \(x\) 所带的辅助点 \(x+m\) 有用的充要条件:存在 \(j\) 满足 \(x< l_j< x+m\),且 \(f[x+m]\) 大于最近的点的 dp 值。
如果不存在这样的 \(j\),很显然 \(f[l_i+m]=f[l_i]\),那么辅助点就没用了。
否则存在这样的 \(j\),我们可以证明 \(l_j+m\) 是没用的,本人的证明比较复杂,此处略。
那么可以直接得到关键点和辅助点总数为 \(O(n)\)。使用堆维护所有的点,使用线段树维护区间 \(+1\),时间复杂度为 \(O(n\log n)\)。
#include <bits/stdc++.h>
#define ll int
#define ull unsigned ll
#define fi first
#define se second
#define pir pair <ll, ll>
#define mkp make_pair
#define pb push_back
using namespace std;
const ll maxn = 2e5 + 10, M = 1e9;
ll n, m, l[maxn], r[maxn];
priority_queue <pir> q;
ll mx[maxn * 45], tag[maxn * 45], lc[maxn * 45], rc[maxn * 45], tot, rt;
void modify(ll p, ll l, ll r, ll ql, ll qr, ll v) {
if(!p) return;
if(ql <= l && r <= qr) {
tag[p] += v, mx[p] += v;
return;
} ll mid = l + r >> 1;
if(ql <= mid) modify(lc[p], l, mid, ql, qr, v);
if(mid < qr) modify(rc[p], mid + 1, r, ql, qr, v);
mx[p] = max(mx[lc[p]], mx[rc[p]]) + tag[p];
}
void rep(ll &p, ll l, ll r, ll x, ll v) {
if(!p) p = ++tot;
if(l == r) {
tag[p] = mx[p] = v;
return;
} ll mid = l + r >> 1;
if(x <= mid) rep(lc[p], l, mid, x, v);
else rep(rc[p], mid + 1, r, x, v);
mx[p] = max(mx[lc[p]], mx[rc[p]]) + tag[p];
}
ll query(ll p, ll l, ll r, ll ql, ll qr) {
if(!p || qr < l || r < ql) return 0;
if(ql <= l && r <= qr) return mx[p];
ll mid = l + r >> 1;
return max(query(lc[p], l, mid, ql, qr), query(rc[p], mid + 1, r, ql, qr)) + tag[p];
}
int main() {
scanf("%d%d", &n, &m);
for(ll i = 1; i <= n; i++) {
scanf("%d%d", l + i, r + i);
if(r[i] < 0) continue;
l[i] = max(l[i], 0);
q.push(mkp(-l[i], 1));
q.push(mkp(-r[i], -l[i]));
}
ll lst = -1;
while(!q.empty()) {
pir t = q.top(); q.pop();
ll l = -t.se, r = -t.fi;
if(l >= 0) {
modify(rt, 0, M, l, r, 1);
continue;
}
ll x = r, typ = l;
if(x == lst) continue;
ll val = (lst == - 1? 0 : query(rt, 0, M, lst, lst)),
tmp = (lst == -1 || x < m? 0 : query(rt, 0, M, 0, x - m));
if(typ == -1 || val < tmp) {
lst = x;
rep(rt, 0, M, x, tmp);
if(x + m <= M) q.push(mkp(-x - m, 2));
}
}
printf("%d", mx[rt]);
return 0;
}

浙公网安备 33010602011771号