1 条题解
-
0
Super Bunny Man!
哎以为自己写了个 结果跑的比别人 还快。吓哭了。后面发现算错了。/咦
题意
给定 个点 , 次询问区间 ,求区间凸包的点数。
,。
思路
注意到一个叫做「存在 ,使得对于所有 ,满足 」的部分分。
这不是分治是啥。于是我们考虑分治,现在我们有一个中心点 ,所有待计算区间都包含这个点。那么现在要做的事情是:
-
计算每个左半部分的后缀凸包,右半部分的前缀凸包。
-
对于一个询问,合并两个凸包。
以计算左半边的后缀凸包为例。每次计算一个后缀 ,就相当于从 的凸包中截取一段后缀 ,和 拼起来, 是 凸包的所有点中与 连线斜率最大的。
注意到我们当然不能直接给每个 开一个
vector存。考虑每个 向其对应的 连一条有向边,那么整个左半边的点会形成一个树状结构, 的凸包就是 的到根链。如何找 对应的 ?在所有 的祖先上倍增即可。右半边同理。接下来如何合并两个凸包 和 ?我们需要找一个直线同时外切两个凸包。在左边凸包上二分一个点 ,找到右边与这个点斜率最大的点 ,看两个部分 和 拼起来之后有没有凹进去。这个依然可以倍增套倍增做。
分析一下时间复杂度:每一层都要 地建出凸包,共 层,时间复杂度 ;每个询问都要倍增套倍增,时间复杂度 ;总时间复杂度 。能过就行。
代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll n, m, h[100002], fa[22][100002], cnt[100002], ans[100002]; struct node { ll l, r, id; }q[100002], q2[100002]; void solve(ll l, ll r, ll qL, ll qR) { if (l > r || qL > qR) return ; ll mid = l + r >> 1, p1 = qL, p2 = qR; for (ll i = qL; i <= qR; i ++ ) { if (q[i].r < mid) q2[p1 ++] = q[i]; else if (q[i].l <= mid) q2[p2 --] = q[i]; } p2 = p1; for (ll i = qL; i <= qR; i ++ ) if (q[i].l > mid) q2[p2 ++] = q[i]; for (ll i = qL; i <= qR; i ++ ) q[i] = q2[i]; solve(l, mid - 1, qL, p1 - 1), solve(mid + 1, r, p1, p2 - 1), qL = p2; if (qL > qR) return ; ll dep = __lg(r - l + 1); for (ll i = 0; i <= dep; i ++ ) fa[i][mid] = 0; cnt[mid] = 1; for (ll x = mid - 1; x >= l; x -- ) { ll p = x + 1; while (p != mid && (h[p] - h[x]) * (fa[0][p] - p) <= (h[fa[0][p]] - h[p]) * (p - x)) p = fa[0][p]; cnt[x] = cnt[fa[0][x] = p] + 1; for (ll i = 1; i <= dep; i ++ ) fa[i][x] = fa[i - 1][fa[i - 1][x]]; } for (ll x = mid + 1; x <= r; x ++ ) { ll p = x - 1; while (p != mid && (h[p] - h[fa[0][p]]) * (x - p) <= (h[x] - h[p]) * (p - fa[0][p])) p = fa[0][p]; cnt[x] = cnt[fa[0][x] = p] + 1; for (ll i = 1; i <= dep; i ++ ) fa[i][x] = fa[i - 1][fa[i - 1][x]]; } for (ll t = qL, ap1, ap2, ap3; t <= qR; t ++ ) { if (q[t].l == mid) { ans[q[t].id] = cnt[q[t].r]; continue; } if (q[t].r == mid) { ans[q[t].id] = cnt[q[t].l]; continue; } p1 = q[t].l; ap1 = ap2 = mid; for (ll i = dep; i >= -1; i -- ) { ll p = (i == -1 ? q[t].l : fa[i][p1]); if (p == mid || !p) continue; p2 = q[t].r; ap3 = mid; for (ll j = dep; j >= -1; j -- ) { ll c = (j == -1 ? q[t].r : fa[j][p2]); if (c == mid || !c) continue; if ((h[c] - h[p]) * (fa[0][c] - p) < (h[fa[0][c]] - h[p]) * (c - p)) p2 = c; else ap3 = c; } if ((h[ap3] - h[p]) * (fa[0][p] - p) < (h[fa[0][p]] - h[p]) * (ap3 - p)) p1 = p; else ap1 = p, ap2 = ap3; } ans[q[t].id] = cnt[q[t].l] + cnt[q[t].r] - cnt[ap1] - cnt[ap2] + 2; } } int main() { cin >> n; for (ll i = 1; i <= n; i ++ ) cin >> h[i]; cin >> m; for (ll i = 1; i <= m; i ++ ) cin >> q[i].l >> q[i].r, q[i].id = i; solve(1, n, 1, m); for (ll i = 1; i <= m; i ++ ) cout << ans[i] - 1 << "\n"; } -
- 1
信息
- ID
- 9649
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者