240102 拳坛咋提 斜率优化

有一种,怎么说,人生追求就要实现的感觉。

这么一想突然感觉自己好没追求啊 hhh


A. 小 P 的牧场

http://222.180.160.110:1024/contest/4624/problem/1

\(f_i\) 表示在 \(i\) 处安一个控制站的最小总费用。那么有:

\[f_i=\min\{f_j+a_i+\sum_{k=j+1}^ib_k\times (i-k)\} \]

然后条件反射令 \(c_i=\sum\limits_{j=1}^i b_j\times (i-j)=c_{i-1}+s_{i-1}\),其中 \(s\)\(b\) 的前缀和。

则有:

\[f_i=\min\{f_j+a_i+c_i-c_j-s_j\times (i-j)\} \]

分参,整理得 \(f_i=\min\{-s_j\times i+s_j\times j-c_j+f_j\}+a_i+c_i\),以 \(k=-s_j\)\(b=s_j\times j-c_j+f_j\) 维护李超,\(x=i\) 时的最低交点即为 \(\min\) 内值。

空间有点卡,注意一下就好了。

#define int long long
namespace XSC062 {
using namespace fastIO;
const int inf = 1e18;
const int maxn = 1e6 + 5;
const int maxm = 1e6 + 5;
#define lt (p << 1)
#define rt (lt | 1)
struct __ { int k, b; };
struct _ { int l, r, u; };
int n, tot;
__ seg[maxn];
_ t[maxm << 2];
int f[maxn], s[maxn];
int a[maxn], b[maxn], c[maxn];
int min(int x, int y) {
	return x < y ? x : y;
}
void swap(int &x, int &y) {
	x ^= y ^= x ^= y;
	return;
}
void bld(int p, int l, int r) {
	t[p].l = l, t[p].r = r, t[p].u = 0;
	if (l == r) return;
	int mid = (l + r) >> 1;
	bld(lt, l, mid), bld(rt, mid + 1, r);
	return;
}
int getv(int id, int x) {
	if (!id) return inf;
	return x * seg[id].k + seg[id].b;
}
void upd(int p, int id) {
	if (!t[p].u) { t[p].u = id; return; }
	int mid = (t[p].l + t[p].r) >> 1;
	int v1 = getv(t[p].u, mid),
		v2 = getv(id, mid);
	if (v2 < v1) swap(t[p].u, id);
	v1 = getv(t[p].u, t[p].l);
	v2 = getv(id, t[p].l);
	if (v2 < v1) upd(lt, id);
	v1 = getv(t[p].u, t[p].r);
	v2 = getv(id, t[p].r);
	if (v2 < v1) upd(rt, id);
	return;
}
void add(int p, int l, int r, int id) {
	if (l <= t[p].l && t[p].r <= r) {
		upd(p, id);
		return;
	}
	int mid = (t[p].l + t[p].r) >> 1;
	if (l <= mid) add(lt, l, r, id);
	if (r > mid) add(rt, l, r, id);
	return;
}
int ask(int p, int x) {
	int res = inf;
	if (t[p].u) res = getv(t[p].u, x);
	if (t[p].l == t[p].r) return res;
	int mid = (t[p].l + t[p].r) >> 1;
	if (x <= mid) res = min(res, ask(lt, x));
	else res = min(res, ask(rt, x));
	return res;
}
int main() {
	read(n);
	bld(1, 1, n);
	add(1, 1, n, ++tot);
	for (int i = 1; i <= n; ++i) read(a[i]);
	for (int i = 1; i <= n; ++i) {
		read(b[i]), s[i] = s[i - 1] + b[i];
		c[i] = c[i - 1] + s[i - 1];
		f[i] = ask(1, i) + a[i] + c[i];
		seg[++tot].k = -s[i];
		seg[tot].b = s[i] * i - c[i] + f[i];
		add(1, 1, n, tot);
	}
	print(f[n], '\n');
	return 0;
}
} // namespace XSC062
#undef int

B. 防御准备

http://222.180.160.110:1024/contest/4624/problem/2

BZOJ3156。

和上一题很像啊。总之为了避免麻烦,我们先把序列 reverse 一下。

我们令 \(f_i\) 表示在 \(i\) 处放守卫塔的最小代价。那么有:

\[f_i=\min\{f_j+ a_i+\sum\limits_{k=j+1}^{i-1}k-j \} \]

那么答案就是 \(f_{n+1}\)。然后 \(\sum\limits_{k=j+1}^{i-1}k-j\) 是个等差数列,长度为 \((i-1)-(j+1)+1\),首、末项分别为 \(1\)\(i-1-j\),则该式值为 \(\dfrac {(i-j)\times (i-j-1)}2\)

则原式等价于:

\[f_i=\min\{-j\times i + \dfrac {j^2+j}2+f_j\}+a_i+\dfrac {i^2-i}2 \]

\(k=-j\)\(b=\dfrac {j^2+j}2+f_j\),则 \(\min\) 内值为 \(x=i\) 时李超的最低交点。

注意题目有必须在 \(1\) 号点(reverse 后)建站的要求。

#define int long long
namespace XSC062 {
using namespace fastIO;
const int inf = 1e18;
const int maxn = 1e6 + 5;
const int maxm = 1e6 + 5;
#define lt (p << 1)
#define rt (lt | 1)
struct __ { int k, b; };
struct _ { int l, r, u; };
int n, tot;
__ seg[maxn];
_ t[maxm << 2];
int f[maxn], a[maxn];
int min(int x, int y) {
	return x < y ? x : y;
}
void swap(int &x, int &y) {
	x ^= y ^= x ^= y;
	return;
}
void bld(int p, int l, int r) {
	t[p].l = l, t[p].r = r, t[p].u = 0;
	if (l == r) return;
	int mid = (l + r) >> 1;
	bld(lt, l, mid), bld(rt, mid + 1, r);
	return;
}
int getv(int id, int x) {
	if (!id) return inf;
	return x * seg[id].k + seg[id].b;
}
void upd(int p, int id) {
	if (!t[p].u) { t[p].u = id; return; }
	int mid = (t[p].l + t[p].r) >> 1;
	int v1 = getv(t[p].u, mid),
		v2 = getv(id, mid);
	if (v2 < v1) swap(t[p].u, id);
	v1 = getv(t[p].u, t[p].l);
	v2 = getv(id, t[p].l);
	if (v2 < v1) upd(lt, id);
	v1 = getv(t[p].u, t[p].r);
	v2 = getv(id, t[p].r);
	if (v2 < v1) upd(rt, id);
	return;
}
void add(int p, int l, int r, int id) {
	if (l <= t[p].l && t[p].r <= r) {
		upd(p, id);
		return;
	}
	int mid = (t[p].l + t[p].r) >> 1;
	if (l <= mid) add(lt, l, r, id);
	if (r > mid) add(rt, l, r, id);
	return;
}
int ask(int p, int x) {
	int res = inf;
	if (t[p].u) res = getv(t[p].u, x);
	if (t[p].l == t[p].r) return res;
	int mid = (t[p].l + t[p].r) >> 1;
	if (x <= mid) res = min(res, ask(lt, x));
	else res = min(res, ask(rt, x));
	return res;
}
int fun(int x) {
	return x * (x - 1) / 2;
}
int main() {
	read(n);
	bld(1, 1, n);
	for (int i = 1; i <= n; ++i)
		read(a[n - i + 1]);
	for (int i = 1; i <= n + 1; ++i) {
		if (i != 1)
			f[i] = ask(1, i) + a[i] + fun(i);
		else f[i] = a[i];
		seg[++tot].k = -i;
		seg[tot].b = fun(i + 1) + f[i];
		add(1, 1, n, tot);
	}
	print(f[n + 1], '\n');
	return 0;
}
} // namespace XSC062
#undef int

C. 土地购买

http://222.180.160.110:1024/contest/4624/problem/3

先按长为第一关键字、高为第二关键字从大到小排个序,然后把可以被完全包含的先剃掉(因为可以将它和包含者捆绑)。

这个时候就有个很有意思的事情,就是由于长是递减的,高一定是递增的(否则就被剃掉了)。

\(f_i\) 表示在 \(i\) 后分隔的最小代价,则有:

\[f_i=\min\{f_j+W_{j+1}\times H_i\} \]

显然令 \(k=W_{j+1}\)\(b=f_j\),答案为李超在 \(x=H_i\) 时的最低交点。

#define int long long
namespace XSC062 {
using namespace fastIO;
const int lim = 1e6;
const int inf = 1e18;
const int maxn = 5e5 + 5;
const int maxm = 1e6 + 5;
#define lt (p << 1)
#define rt (lt | 1)
struct __ { int k, b; };
struct ___ { int h, w; };
struct _ { int l, r, u; };
___ a[maxn];
int f[maxn];
__ seg[maxn];
_ t[maxm << 2];
int n, tot, cnt;
int min(int x, int y) {
	return x < y ? x : y;
}
void swap(int &x, int &y) {
	x ^= y ^= x ^= y;
	return;
}
void bld(int p, int l, int r) {
	t[p].l = l, t[p].r = r, t[p].u = 0;
	if (l == r) return;
	int mid = (l + r) >> 1;
	bld(lt, l, mid), bld(rt, mid + 1, r);
	return;
}
int getv(int id, int x) {
	if (!id) return inf;
	return x * seg[id].k + seg[id].b;
}
void upd(int p, int id) {
	if (!t[p].u) { t[p].u = id; return; }
	int mid = (t[p].l + t[p].r) >> 1;
	int v1 = getv(t[p].u, mid),
		v2 = getv(id, mid);
	if (v2 < v1) swap(t[p].u, id);
	v1 = getv(t[p].u, t[p].l);
	v2 = getv(id, t[p].l);
	if (v2 < v1) upd(lt, id);
	v1 = getv(t[p].u, t[p].r);
	v2 = getv(id, t[p].r);
	if (v2 < v1) upd(rt, id);
	return;
}
void add(int p, int l, int r, int id) {
	if (l <= t[p].l && t[p].r <= r) {
		upd(p, id);
		return;
	}
	int mid = (t[p].l + t[p].r) >> 1;
	if (l <= mid) add(lt, l, r, id);
	if (r > mid) add(rt, l, r, id);
	return;
}
int ask(int p, int x) {
	int res = inf;
	if (t[p].u) res = getv(t[p].u, x);
	if (t[p].l == t[p].r) return res;
	int mid = (t[p].l + t[p].r) >> 1;
	if (x <= mid) res = min(res, ask(lt, x));
	else res = min(res, ask(rt, x));
	return res;
}
int fun(int x) {
	return x * (x - 1) / 2;
}
int main() {
	read(n);
	bld(1, 1, lim);
	for (int i = 1; i <= n; ++i)
		read(a[i].h), read(a[i].w);
	std::sort(a + 1, a + n + 1,
	[&](___ x, ___ y) {
		return x.w == y.w ?
		x.h > y.h : x.w > y.w; } );
	for (int i = 1; i <= n; ++i) {
		if (a[i].h > a[cnt].h)
			a[++cnt] = a[i];
	}
	for (int i = 0; i <= cnt; ++i) {
		if (i) f[i] = ask(1, a[i].h);
		if (i != cnt) {
			seg[++tot].k = a[i + 1].w;
			seg[tot].b = f[i];
			add(1, 1, lim, tot);
		}
	}
	print(f[cnt], '\n');
	return 0;
}
} // namespace XSC062
#undef int

总之就是我们发扬传统艺能,在一个晚自习用李超草过了三道斜优。🤔

posted @ 2024-01-02 22:17  XSC062  阅读(21)  评论(2编辑  收藏  举报