总览:
维护若干一次函数的最值
李超线段树的结构和普通线段树一样的,只是它每个节点存的是该区间优势最大的线段(优势最大即暴露在最高折线中横坐标跨度最大)
李超线段树并不严格,只需满足包括单点的所有线段树区间“优势最大线段”中含有该单点的优势最大线段即可。
会出现如果一整个区间最高折线都被一条线段占了的话,只有最大的区间的“优势最大线段”是该线段的情况
插入时每个区间分类讨论
单点查询时遍历所有区间
T1 P4254 [JSOI2008]Blue Mary开公司
思路:板子题
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define pb push_back
#define mp make_pair
namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch() \
(p1_ == p2_ && \
(p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
? EOF \
: *p1_++)
inline int in() {
int s = 0, f = 1;
char x = ch();
for (; x < '0' || x > '9'; x = ch())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _p1 = -1;
inline void flush() { fwrite(_buf, 1, _p1 + 1, stdout), _p1 = -1; }
inline void pc(char x) {
if (_p1 == (1 << 21) - 1) flush();
_buf[++_p1] = x;
}
inline void out(int x) {
char k[30];
int pos = 0;
if (!x) return pc('0');
if (x < 0) pc('-'), x = -x;
while (x) k[++pos] = (x % 10) | 48, x /= 10;
for (int i = pos; i; i--) pc(k[i]);
}
} // namespace IO
using namespace IO;
const int A = 1e5 + 5;
const int MAX = 5e4;
int n;
char opt[30];
struct node {
double k, b;
} p[A];
int tot;
inline int New(double x, double y) {
++tot;
p[tot].k = y, p[tot].b = x - y;
return tot;
}
inline double calc(int id, int x) { return 1.0 * p[id].k * x + p[id].b; }
inline double inter(int x, int y) {
return (p[y].b - p[x].b) / (p[x].k - p[y].k);
}
struct SGT {
int l, r;
int s;
} tr[4 * A];
#define ls x << 1
#define rs x << 1 | 1
inline void build(int x, int l, int r) {
tr[x].l = l, tr[x].r = r;
if (l == r) return;
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
return;
}
inline void insert(int x, int l, int r, int u) {
int mid = (tr[x].l + tr[x].r) >> 1;
if (tr[x].l >= l && tr[x].r <= r) {
if (!tr[x].s) {
tr[x].s = u;
return;
}
int v = tr[x].s;
double ly = calc(v, tr[x].l), ry = calc(v, tr[x].r), lyn = calc(u, tr[x].l),
ryn = calc(u, tr[x].r);
if (ly >= lyn && ry >= ryn) return;
if (ly <= lyn && ry <= ryn) {
tr[x].s = u;
return;
}
double xx = inter(u, v);
if (ly > lyn) {
if (xx <= mid)
tr[x].s = u, insert(ls, l, r, v);
else
insert(rs, l, r, u);
} else {
if (xx <= mid)
insert(ls, l, r, u);
else
tr[x].s = u, insert(rs, l, r, v);
}
return;
}
if (l <= mid) insert(ls, l, r, u);
if (r >= mid + 1) insert(rs, l, r, u);
return;
}
inline int Max(int x, int y, int w) { return calc(x, w) > calc(y, w) ? x : y; }
inline int qurey(int x, int w) {
if (tr[x].l == tr[x].r) return tr[x].s;
int res = tr[x].s;
int mid = (tr[x].l + tr[x].r) >> 1;
if (w <= mid)
res = Max(res, qurey(ls, w), w);
else
res = Max(res, qurey(rs, w), w);
return res;
}
#undef ls
#undef rs
signed main() {
scanf("%d", &n);
build(1, 1, MAX);
for (int i = 1; i <= n; i++) {
scanf("%s", opt);
if (opt[0] == 'P') {
double u, v;
scanf("%lf%lf", &u, &v);
insert(1, 1, MAX, New(u, v));
} else {
int k;
scanf("%d", &k);
printf("%d\n", (int)(calc(qurey(1, k), k) / 100));
}
}
flush();
return 0;
}
T2 P4097 [HEOI2013]Segment
思路:板子题
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define pb push_back
#define mp make_pair
#define int long long
namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch() \
(p1_ == p2_ && \
(p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
? EOF \
: *p1_++)
inline int in() {
int s = 0, f = 1;
char x = ch();
for (; x < '0' || x > '9'; x = ch())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _p1 = -1;
inline void flush() { fwrite(_buf, 1, _p1 + 1, stdout), _p1 = -1; }
inline void pc(char x) {
if (_p1 == (1 << 21) - 1) flush();
_buf[++_p1] = x;
}
inline void out(int x) {
char k[30];
int pos = 0;
if (!x) return pc('0');
if (x < 0) pc('-'), x = -x;
while (x) k[++pos] = (x % 10) | 48, x /= 10;
for (int i = pos; i; i--) pc(k[i]);
}
} // namespace IO
using namespace IO;
const int A = 4e5 + 5;
int n;
struct node {
double k, b;
} p[A];
int tot;
inline void New(int x1, int y1, int x2, int y2) {
int x = ++tot;
if (x1 == x2)
p[x].k = 0, p[x].b = max(y1, y2);
else
p[x].k = 1.0 * (y2 - y1) / (x2 - x1), p[x].b = 1.0 * y1 - 1.0 * x1 * p[x].k;
return;
}
inline double calc(int id, int x) { return 1.0 * p[id].k * x + p[id].b; }
inline double inter(int x, int y) {
return (p[x].b - p[y].b) / (p[y].k - p[x].k);
}
inline int check(int x, int y, int w) {
return calc(x, w) > calc(y, w) ? x : y;
}
struct SGT {
int l, r;
int s;
} tr[4 * A];
#define ls x << 1
#define rs x << 1 | 1
inline void build(int x, int l, int r) {
tr[x].l = l, tr[x].r = r;
if (l == r) return;
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
return;
}
inline void insert(int x, int l, int r, int u) {
int mid = (tr[x].l + tr[x].r) >> 1;
if (tr[x].l >= l && tr[x].r <= r) {
if (!tr[x].s) {
tr[x].s = u;
return;
}
int v = tr[x].s;
double ly = calc(v, tr[x].l), ry = calc(v, tr[x].r), lyn = calc(u, tr[x].l),
ryn = calc(u, tr[x].r);
if (ly >= lyn && ry >= ryn) return;
if (lyn >= ly && ryn >= ry) {
tr[x].s = u;
return;
}
double xx = inter(u, v);
if (ly > lyn) {
if (xx <= mid)
insert(ls, l, r, v), tr[x].s = u;
else
insert(rs, l, r, u);
} else {
if (xx <= mid)
insert(ls, l, r, u);
else
insert(rs, l, r, v), tr[x].s = u;
}
}
if (l <= mid) insert(ls, l, r, u);
if (r >= mid + 1) insert(rs, l, r, u);
return;
}
inline int find(int x, int w) {
if (tr[x].l == tr[x].r) return tr[x].s;
int res = tr[x].s;
int mid = (tr[x].l + tr[x].r) >> 1;
if (w <= mid)
res = check(res, find(ls, w), w);
else
res = check(res, find(rs, w), w);
return res;
}
#undef ls
#undef rs
signed main() {
n = in();
build(1, 1, 39989);
int ans = 0;
for (int i = 1; i <= n; i++) {
int opt = in();
if (!opt) {
int x = (in() + ans - 1) % 39989 + 1;
out(ans = find(1, x)), pc('\n');
} else {
int x1 = (in() + ans - 1) % 39989 + 1,
y1 = (in() + ans - 1) % 1000000000 + 1,
x2 = (in() + ans - 1) % 39989 + 1,
y2 = (in() + ans - 1) % 1000000000 + 1;
if (x1 > x2) swap(x1, x2), swap(y1, y2);
New(x1, y1, x2, y2);
insert(1, x1, x2, tot);
}
}
flush();
return 0;
}
T3 P4069 [SDOI2016]游戏
思路:
树剖+李超线段树
对于
A
∗
d
i
s
+
B
A*dis+B
A∗dis+B,将它分成
s
−
>
l
c
a
,
l
c
a
−
>
t
s->lca,lca->t
s−>lca,lca−>t
设
d
[
i
]
d[i]
d[i] 为
i
i
i 到根的距离
s
−
>
l
c
a
:
A
∗
(
d
[
s
]
−
d
[
x
]
)
+
B
=
−
A
∗
d
[
x
]
+
A
∗
d
[
s
]
+
B
l
c
a
−
>
t
:
A
∗
(
d
[
s
]
+
d
[
x
]
−
2
∗
d
[
l
c
a
]
)
+
B
=
A
∗
d
[
x
]
+
A
∗
d
[
s
]
−
2
∗
A
∗
d
[
l
c
a
]
+
B
s->lca:\\ A*(d[s]-d[x])+B=-A*d[x]+A*d[s]+B\\ lca->t:\\ A*(d[s]+d[x]-2*d[lca])+B=A*d[x]+A*d[s]-2*A*d[lca]+B
s−>lca:A∗(d[s]−d[x])+B=−A∗d[x]+A∗d[s]+Blca−>t:A∗(d[s]+d[x]−2∗d[lca])+B=A∗d[x]+A∗d[s]−2∗A∗d[lca]+B
注意每个下标的点值是到根的距离
维护区间最小就每次用优势最大线段的两头更新答案
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define pb push_back
#define mp make_pair
#define int long long
namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch() \
(p1_ == p2_ && \
(p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
? EOF \
: *p1_++)
inline int in() {
int s = 0, f = 1;
char x = ch();
for (; x < '0' || x > '9'; x = ch())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _p1 = -1;
inline void flush() { fwrite(_buf, 1, _p1 + 1, stdout), _p1 = -1; }
inline void pc(char x) {
if (_p1 == (1 << 21) - 1) flush();
_buf[++_p1] = x;
}
inline void out(int x) {
char k[30];
int pos = 0;
if (!x) return pc('0');
if (x < 0) pc('-'), x = -x;
while (x) k[++pos] = (x % 10) | 48, x /= 10;
for (int i = pos; i; i--) pc(k[i]);
}
} // namespace IO
using namespace IO;
const int A = 4e5 + 5;
const int INF = 123456789123456789;
int n, m;
int head[A], tot_road;
struct Road {
int nex, to, w;
} road[2 * A];
inline void edge(int x, int y, int w) {
road[++tot_road] = {head[x], y, w}, head[x] = tot_road;
}
int f[A], dis[A], dep[A], sz[A], son[A], top[A], pos[A], idx[A], tot_cut;
inline void DFS1(int fa, int x) {
f[x] = fa, dep[x] = dep[fa] + 1, sz[x] = 1;
for (int y = head[x]; y; y = road[y].nex) {
int z = road[y].to, w = road[y].w;
if (z == fa) continue;
dis[z] = dis[x] + w;
DFS1(x, z);
sz[x] += sz[z];
if (sz[z] > sz[son[x]]) son[x] = z;
}
return;
}
inline void DFS2(int x) {
pos[x] = ++tot_cut, idx[pos[x]] = x;
if (son[x]) {
top[son[x]] = top[x];
DFS2(son[x]);
}
for (int y = head[x]; y; y = road[y].nex) {
int z = road[y].to;
if (top[z]) continue;
top[z] = z;
DFS2(z);
}
return;
}
inline int lca(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
x = f[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
inline void tree_cut() {
DFS1(0, 1);
top[1] = 1;
DFS2(1);
return;
}
struct node {
int k, b;
} p[A];
int tot_node;
inline int New(int k, int b) {
int x = ++tot_node;
p[x].k = k, p[x].b = b;
return x;
}
inline int calc(int id, int x) { return p[id].k * dis[idx[x]] + p[id].b; }
struct SGT {
int l, r;
int s, mn;
} tr[4 * A];
#define ls x << 1
#define rs x << 1 | 1
inline void build(int x, int l, int r) {
tr[x].l = l, tr[x].r = r;
tr[x].s = 1, tr[x].mn = INF;
if (l == r) return;
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
return;
}
inline void pushup(int x) {
tr[x].mn = min(tr[x].mn, min(tr[ls].mn, tr[rs].mn));
}
inline void insert(int x, int l, int r, int u) {
int mid = (tr[x].l + tr[x].r) >> 1;
if (tr[x].l >= l && tr[x].r <= r) {
if (!tr[x].s) {
tr[x].s = u;
tr[x].mn = min(tr[x].mn, min(calc(u, tr[x].l), calc(u, tr[x].r)));
return;
}
int v = tr[x].s;
int ly = calc(v, tr[x].l), ry = calc(v, tr[x].r), lyn = calc(u, tr[x].l),
ryn = calc(u, tr[x].r);
if (ly <= lyn && ry <= ryn) return;
if (ly >= lyn && ry >= ryn) {
tr[x].s = u;
tr[x].mn = min(tr[x].mn, min(calc(u, tr[x].l), calc(u, tr[x].r)));
return;
}
int my = calc(v, mid), myn = calc(u, mid);
if (ly > lyn) {
if (my > myn)
insert(rs, l, r, v), tr[x].s = u;
else
insert(ls, l, r, u);
} else {
if (my > myn)
insert(ls, l, r, v), tr[x].s = u;
else
insert(rs, l, r, u);
}
tr[x].mn = min(tr[x].mn, min(calc(u, tr[x].l), calc(u, tr[x].r)));
pushup(x);
return;
}
if (l <= mid) insert(ls, l, r, u);
if (r >= mid + 1) insert(rs, l, r, u);
pushup(x);
return;
}
inline int find(int x, int l, int r) {
if (tr[x].l >= l && tr[x].r <= r) return tr[x].mn;
int ans = INF;
int mid = (tr[x].l + tr[x].r) >> 1;
if (p[tr[x].s].b != INF)
ans = min(ans, min(calc(tr[x].s, max(tr[x].l, l)),
calc(tr[x].s, min(tr[x].r, r))));
if (l <= mid) ans = min(ans, find(ls, l, r));
if (r >= mid + 1) ans = min(ans, find(rs, l, r));
return ans;
}
#undef ls
#undef rs
inline void road_add(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
insert(1, pos[top[x]], pos[x], tot_node);
x = f[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
insert(1, pos[x], pos[y], tot_node);
return;
}
inline int road_qurey(int x, int y) {
int ans = INF;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
ans = min(ans, find(1, pos[top[x]], pos[x]));
x = f[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
ans = min(ans, find(1, pos[x], pos[y]));
return ans;
}
signed main() {
n = in(), m = in();
for (int i = 1; i < n; i++) {
int u = in(), v = in(), w = in();
edge(u, v, w), edge(v, u, w);
}
tree_cut();
New(0, INF);
build(1, 1, n);
while (m--) {
int opt = in();
if (opt == 1) {
int s = in(), t = in(), a = in(), b = in();
New(-a, a * dis[s] + b);
road_add(s, lca(s, t));
New(a, a * (dis[s] - 2 * dis[lca(s, t)]) + b);
road_add(lca(s, t), t);
} else {
int s = in(), t = in();
out(road_qurey(s, t)), pc('\n');
}
}
flush();
return 0;
}
T4 P4655 [CEOI2017]Building Bridges
思路:
李超线段树还可以维护斜率优化
虽然复杂度多个log
f
i
=
a
i
×
b
j
+
c
i
+
d
j
f_i=a_i\times b_j+c_i+d_j
fi=ai×bj+ci+dj
转化成
f
i
+
c
i
=
b
j
×
a
i
+
d
j
f_i+c_i=b_j\times a_i+d_j
fi+ci=bj×ai+dj
后面就是一个一次函数在
a
i
a_i
ai 处的最值
这道题的式子
s
n
=
∑
w
i
s_n=\sum w_i
sn=∑wi
f
i
=
min
{
f
j
+
h
i
2
+
h
j
2
−
2
h
i
h
j
+
s
i
−
1
−
s
j
}
f
i
=
h
i
2
+
s
i
−
1
+
min
{
−
2
h
j
h
i
+
f
j
+
h
j
2
−
s
j
}
f_i=\min \{f_j+h_i^2+h_j^2-2h_ih_j+s_{i-1}-s_j\}\\ \\ f_i=h_i^2+s_{i-1}+\min \{-2h_jh_i+f_j+h_j^2-s_j\}
fi=min{fj+hi2+hj2−2hihj+si−1−sj}fi=hi2+si−1+min{−2hjhi+fj+hj2−sj}
转移就完了
以后再也不用写动态凸包/cdq分治辣
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define pb push_back
#define mp make_pair
#define int long long
namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch() \
(p1_ == p2_ && \
(p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
? EOF \
: *p1_++)
inline int in() {
int s = 0, f = 1;
char x = ch();
for (; x < '0' || x > '9'; x = ch())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _p1 = -1;
inline void flush() { fwrite(_buf, 1, _p1 + 1, stdout), _p1 = -1; }
inline void pc(char x) {
if (_p1 == (1 << 21) - 1) flush();
_buf[++_p1] = x;
}
inline void out(int x) {
char k[30];
int pos = 0;
if (!x) return pc('0');
if (x < 0) pc('-'), x = -x;
while (x) k[++pos] = (x % 10) | 48, x /= 10;
for (int i = pos; i; i--) pc(k[i]);
}
} // namespace IO
using namespace IO;
const int A = 1e5 + 5;
const int INF = 1e12;
int n;
int h[A], hh[A], w[A], s[A], len;
int f[A];
inline void lsh() {
sort(hh + 1, hh + 1 + n);
len = unique(hh + 1, hh + 1 + n) - (hh + 1);
for (int i = 1; i <= n; i++)
h[i] = lower_bound(hh + 1, hh + 1 + len, h[i]) - hh;
return;
}
struct node {
int k, b;
} p[A];
int tot_node;
inline void New(int k, int b) {
int x = ++tot_node;
p[x].k = k, p[x].b = b;
return;
}
inline int calc(int id, int w) { return p[id].k * hh[w] + p[id].b; }
struct SGT {
int l, r;
int s;
} tr[4 * A];
#define ls x << 1
#define rs x << 1 | 1
inline void build(int x, int l, int r) {
tr[x].l = l, tr[x].r = r;
if (l == r) return;
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
return;
}
inline void insert(int x, int l, int r, int u) {
int mid = (tr[x].l + tr[x].r) >> 1;
if (tr[x].l >= l && tr[x].r <= r) {
if (!tr[x].s) {
tr[x].s = u;
return;
}
int v = tr[x].s;
int ly = calc(v, tr[x].l), ry = calc(v, tr[x].r);
int lyn = calc(u, tr[x].l), ryn = calc(u, tr[x].r);
if (ly <= lyn && ry <= ryn) return;
if (ly >= lyn && ry >= ryn) {
tr[x].s = u;
return;
}
int my = calc(v, mid), myn = calc(u, mid);
if (ly <= lyn) {
if (my >= myn)
insert(ls, l, r, v), tr[x].s = u;
else
insert(rs, l, r, u);
} else {
if (my >= myn)
insert(rs, l, r, v), tr[x].s = u;
else
insert(ls, l, r, u);
}
return;
}
if (l <= mid) insert(ls, l, r, u);
if (r >= mid + 1) insert(rs, l, r, u);
return;
}
inline int Max(int x, int y, int w) { return calc(x, w) <= calc(y, w) ? x : y; }
inline int find(int x, int w) {
if (tr[x].l == tr[x].r) return tr[x].s;
int res = tr[x].s;
int mid = (tr[x].l + tr[x].r) >> 1;
if (w <= mid)
res = Max(res, find(ls, w), w);
else
res = Max(res, find(rs, w), w);
return res;
}
#undef ls
#undef rs
signed main() {
n = in();
for (int i = 1; i <= n; i++) h[i] = hh[i] = in();
for (int i = 1; i <= n; i++) w[i] = in(), s[i] = s[i - 1] + w[i];
lsh();
build(1, 1, len);
p[0].k = p[0].b = INF;
New(-2 * hh[h[1]], f[1] + hh[h[1]] * hh[h[1]] - s[1]);
insert(1, 1, len, tot_node);
for (int i = 2; i <= n; i++) {
int val = calc(find(1, h[i]), h[i]);
f[i] = hh[h[i]] * hh[h[i]] + s[i - 1] + val;
New(-2 * hh[h[i]], f[i] + hh[h[i]] * hh[h[i]] - s[i]);
insert(1, 1, n, tot_node);
}
out(f[n]), pc('\n');
flush();
return 0;
}