ABC370 F - Cake Division

ABC370 F - Cake Division

给定一个长度为 n 的环,请你分成 k 段,并最大化 min(w1,w2,,wk),其中 wi 是第 i 段的权值和。同时你还需要回答在所有可行方案中,有多少处位置一定不会被断开。

看到最小值最大,想到二分。

对于环上问题,容易想到断环成链。

因为并不需要求方案数,所以对于每个 i,我们只需要跳到第一个满足 s[j]s[i]k 的位置即可 。

现在我们需要快速的求解是否从某点断开后,能不能在序列上跳 k 次。

看见 “跳”,想到倍增优化dpf[i][0] 就是 s[f[i][0]]s[i1]k 的第一个位置。

时间复杂度 O(nlogn2)

#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l);i<=(r);++i)
#define G(i,r,l) for(int i(r);i>=(l);--i)
using namespace std;
using ll = long long;
const int N = 4e5 + 1005;
int f[N][19], a[N];
ll sum[N];
int n, k, maxn;
bool ck(int l, int r, int lim){
	int tot = k, nw = l;
	G(i, maxn, 0){
		if(nw > r) return 0;
		if(tot < (1 << i) || f[nw][i] > r) continue;
		tot -= (1 << i);	
		nw = f[nw][i] + 1;
		if(!tot) break;
	} return tot == 0;
}
void init(ll lim){
	for(int i = 1, j = 1; i <= n; ++ i){
		while(j <= 2 * n && sum[j] - sum[i - 1] < lim) ++ j;
		f[i][0] = j;
		f[i + n][0] = min(2 * n + 1, j + n);
	}
	F(j, 1, maxn) F(i, 1, n * 2) f[i][j] = f[i][j - 1] + 1 > n * 2 ? 2 * n + 1 : f[f[i][j - 1] + 1][j - 1];
}
bool check(int lim){
	init(lim);
	F(i, 1, n) if(ck(i, i + n - 1, lim)) return 1;	
	return 0;
}
signed main(){
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> n >> k;
	F(i, 1, n) cin >> a[i], a[i + n] = a[i];
	F(i, 1, n * 2) sum[i] = sum[i - 1] + a[i];
	maxn = __lg(2 * n);
	ll l = 0, r = sum[n] + 1, mid;
	while(l  + 1 < r){
		mid = (l + r) >> 1;
		if(check(mid)) l = mid;
		else r = mid;
	}
	int ans1 = l, ans2 = 0;
	init(ans1);
	F(i, 1, n) ans2 += ck(i, i + n - 1, ans1);
	cout << ans1 << ' ' << n - ans2 << '\n';
	return fflush(0), 0;
}
posted @   superl61  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示