1 条题解

  • 0
    @ 2026-4-23 23:33:47

    Super Bunny Man!

    哎以为自己写了个 3log3\log 结果跑的比别人 2log2\log 还快。吓哭了。后面发现算错了。/咦

    题意

    给定 nn 个点 (i,hi)(i,h_i)qq 次询问区间 l,rl,r,求区间凸包的点数。

    n,q105n,q\le 10^5hi1012h_i\le 10^{12}

    思路

    注意到一个叫做「存在 kk,使得对于所有 ii,满足 likril_i \leq k \leq r_i」的部分分。

    这不是分治是啥。于是我们考虑分治,现在我们有一个中心点 midmid,所有待计算区间都包含这个点。那么现在要做的事情是:

    1. 计算每个左半部分的后缀凸包,右半部分的前缀凸包。

    2. 对于一个询问,合并两个凸包。

    以计算左半边的后缀凸包为例。每次计算一个后缀 xmidx\sim mid,就相当于从 x+1midx+1\sim mid 的凸包中截取一段后缀 ymidy\sim mid,和 xx 拼起来,yyx+1midx+1\sim mid 凸包的所有点中与 xx 连线斜率最大的。

    注意到我们当然不能直接给每个 xx 开一个 vector 存。考虑每个 xx 向其对应的 yy 连一条有向边,那么整个左半边的点会形成一个树状结构xmidx\sim mid 的凸包就是 xx到根链。如何找 xx 对应的 yy?在所有 x+1x+1 的祖先上倍增即可。右半边同理。

    接下来如何合并两个凸包 lmidl\sim midmidrmid\sim r?我们需要找一个直线同时外切两个凸包。在左边凸包上二分一个点 xx^\prime,找到右边与这个点斜率最大的点 yy^\prime,看两个部分 lxl\sim x^\primeyry^\prime\sim r 拼起来之后有没有凹进去。这个依然可以倍增套倍增做。

    分析一下时间复杂度:每一层都要 O(nlogn)O(n\log n) 地建出凸包,共 logn\log n 层,时间复杂度 O(nlog2n)O(n\log^2 n);每个询问都要倍增套倍增,时间复杂度 O(qlog2n)O(q\log^2 n);总时间复杂度 O((n+q)log2n)O((n+q)\log^2n)。能过就行。

    代码

    #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
    上传者