动态凸包
是什么
动态凸包就是在可增删直线的情况下,求凸包(或凸包上的点)。
怎么求
- 离线所有操作
- 记录每一条直线出现和消失的时间
- 按照时间线建立线段树
- 在线段树上对应节点上插入直线(vector存储)
- 统一处理所有询问,即在每一个节点上,对该节点上的直线求凸包,然后对属于该区间的所有询问进行更新。求每一个位置时,在凸包上二分。
题目
hdu 6770杭电多校2020第二场H
题意:动态插入或删除一些形如
f
i
(
x
)
=
(
x
−
a
i
)
4
+
b
i
f_{i}(x) = (x - a_{i})^{4} +b_{i}
fi(x)=(x−ai)4+bi的函数,询问某一时刻,某一x对应的函数的最小值。
思路:
g
(
x
)
=
f
i
(
x
)
−
f
j
(
x
)
=
−
4
(
a
i
−
a
j
)
x
3
+
6
(
a
i
2
−
a
j
2
)
x
2
−
4
(
a
i
3
−
a
j
3
)
x
+
(
a
i
4
−
a
j
4
)
+
(
b
i
−
b
j
)
g(x) =f_{i}(x)-f_{j}(x)=-4(a_{i}-a_{j})x^{3}+6(a_{i}^{2}-a_{j}^{2})x^{2}-4(a_{i}^{3}-a_{j}^{3})x+(a_{i}^{4}-a_{j}^{4})+(b_{i}-b_{j})
g(x)=fi(x)−fj(x)=−4(ai−aj)x3+6(ai2−aj2)x2−4(ai3−aj3)x+(ai4−aj4)+(bi−bj),其中
a
i
<
a
j
a_{i}<a_{j}
ai<aj。
g
′
(
x
)
=
−
12
(
a
i
−
a
j
)
x
2
+
12
(
a
i
2
−
a
j
2
)
x
−
4
(
a
i
3
−
a
j
3
)
=
−
3
(
a
i
−
a
j
)
(
3
x
2
−
3
(
a
i
+
a
j
)
x
+
(
a
i
2
+
a
i
a
j
+
a
j
2
)
)
g'(x)=-12(a_{i}-a_{j})x^{2}+12(a_{i}^{2}-a_{j}^{2})x-4(a_{i}^{3}-a_{j}^{3})=-3(a_{i}-a_{j})(3x^{2}-3(a_{i}+a_{j})x+(a_{i}^{2}+a_{i}a_{j}+a_{j}^{2}))
g′(x)=−12(ai−aj)x2+12(ai2−aj2)x−4(ai3−aj3)=−3(ai−aj)(3x2−3(ai+aj)x+(ai2+aiaj+aj2))。
Δ
=
9
(
a
i
−
a
j
)
3
≤
0
\Delta=9(a_{i}-a_{j})^{3}\leq0
Δ=9(ai−aj)3≤0,所以g(x)单调递减,因此任意
f
i
f_{i}
fi与
f
j
f_{j}
fj只有一个交点,并且在交点左侧
f
i
<
f
j
f_{i}<f_{j}
fi<fj,在交点右侧
f
i
>
f
j
f_{i}>f_{j}
fi>fj。因此这一类四次函数有了和直线相似的性质,这道题目也就转化成了动态凸包的问题。
求交点:二分求交点。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
const LL inf = 9e18;
vector <int> vec[N * 4];
int S[N * 2], E[N * 2], st[N * 2], que[N * 2];
LL A[N * 2], B[N * 2], ans[N * 2], Q[N * 2], pst[N * 2];
void update(int x, int l, int r, int ll, int rr, int t)
{
if(r < ll || rr < l) return;
if(ll <= l && r <= rr) { vec[x].push_back(t); return; }
int mid = (l + r) >> 1;
update(x * 2, l, mid, ll, rr, t);
update(x * 2 + 1, mid + 1, r, ll, rr, t);
}
LL get_f(int id, LL x)
{
LL a = A[id], b = B[id];
return (x - a) * (x - a) * (x - a) * (x - a) + b;
}
LL get_point(int x, int y)
{
LL l = 0, r = 50001;
while(r - l > 1)
{
LL mid = (l + r) >> 1;
if(get_f(x, mid) > get_f(y, mid)) r = mid;
else l = mid;
}
return l;
}
LL bi_search(int top, LL x)
{
int l = 1, r = top + 1;
while(r - l > 1)
{
int mid = (l + r) >> 1;
if(pst[mid] < x) l = mid;
else r = mid;
}
LL a = A[st[l]], b = B[st[l]];
return (x - a) * (x - a) * (x - a) * (x - a) + b;
}
int cmp(int x, int y){ return A[x] < A[y];}
void pushdown(int x, int l, int r)
{
if(vec[x].size())
{
sort(vec[x].begin(), vec[x].end(), cmp);
st[1] = vec[x][0];
int top = 1;
for(int i = 1; i < vec[x].size(); i++)
{
while(top > 1 && pst[top] >= get_point(st[top], vec[x][i])) top--;
st[++top] = vec[x][i];
pst[top] = get_point(st[top - 1], st[top]);
}
for(int i = l; i <= r; i++)
if(Q[i])
ans[i] = min(ans[i], bi_search(top, Q[i]));
}
if(l == r) return;
int mid = (l + r) >> 1;
pushdown(x * 2, l, mid);
pushdown(x * 2 + 1, mid + 1, r);
}
int main()
{
int T, n, m;
scanf("%d", &T);
while(T)
{
T--;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
{
scanf("%lld%lld", &A[i], &B[i]);
S[i] = 1;
}
int cnt = 0;
for(int i = 1; i <= m; i++)
{
Q[i] = 0;
int opt, t;
LL x;
scanf("%d", &opt);
switch(opt)
{
case 1:
n++; scanf("%lld%lld", &A[n], &B[n]); S[n] = i; break;
case 2:
scanf("%d", &t); E[t] = i; break;
case 3:
scanf("%lld", &x); Q[i] = x; que[++cnt] = i; break;
}
}
for(int i = 1; i <= m * 4; i++)
vec[i].clear();
for(int i = 1; i <= n; i++)
{
if(!E[i]) E[i] = m;
update(1, 1, m, S[i], E[i], i);
}
A[n + 1] = 0;
B[n + 1] = 0;
for(int i = 1; i <= m; i++)
ans[i] = inf;
pushdown(1, 1, m);
for(int i = 1; i <= cnt; i++)
{
if(ans[que[i]] == inf) cout<<"-1"<<endl;
else cout<<ans[que[i]]<<endl;
}
for(int i = 1; i <= n; i++) E[i] = 0;
}
return 0;
}