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;
}