Loading

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;
}
posted @ 2024-05-24 20:11  Lgx_Q  阅读(10)  评论(0编辑  收藏  举报