CF526E Transmitting Levels

题目大意:给出环上 \(n\) 个正整数。\(q\) 此询问,每次给定 \(L\),分成尽量少的段,使每段长度和 \(\le L\)

\(n \le 10^6, q \leq 50\)

将环展开成长度为 \(2n\) 的序列,令 \(nxt_i\) 表示以 \(i\) 开头同块最远端。

双指针求出后枚举起点,暴力向后跳,时间复杂度 \(O(n^2)\)

\(f_{i, j}\) 表示从 \(i\) 开始 \(2^j\) 块的最远端,\(f_{i, 0} = nxt_i\),通过倍增预处理。

对于每个起点倍增向后条,时间复杂度 \(O(qn \log n)\),仍不可过。

取任意起点,暴力向后跳,设原来的环被分成了 \(m\) 段,最小的一段长度 \(\le \frac nm\)

枚举该段上的点类似 check,每次不长不超过 \(O(m)\)

时间复杂度 \(O(q \times \frac nm \times m) = O(qn)\)

#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f3f3f3f3f
typedef long long ll;
ll n, q, L, a[2000005], nxt[2000005];
 
int main() {
    scanf("%lld%lld", &n, &q);
    for (ll i = 1; i <= n; i++) {
        scanf("%lld", a + i);
        a[i + n] = a[i];
    }
    while (q--) {
        scanf("%lld", &L);
        for (ll i = 1, j = 1, sum = 0; i <= 2 * n; i++) {
            while (j <= 2 * n && sum + a[j] <= L)
                sum += a[j++];
            nxt[i] = j; sum -= a[i];
        }
        ll pos = 1, ans = inf;
        for (ll i = 2; i <= n; i++) {
            if (nxt[i] - i < nxt[pos] - pos)
                pos = i;
        }
        for (ll s = pos + 1; s <= nxt[pos]; s++) {
            ll t = s - n * (s > n), l = t, res = 0;
            while (l <= t + n - 1)
                res++, l = nxt[l];
            ans = min(ans, res);
        }
        printf("%lld\n", ans);
    }
    return 0;
}
posted @ 2023-03-11 21:52  CodingShark  阅读(27)  评论(0编辑  收藏  举报