题解:AT_abc480_f [ABC480F] Cake Cutting
lhm_gdf7nh8 · · 题解
ABC480F 题解
题意简述
有
你可以任意次选择一块蛋糕切成两块。
最终要将所有蛋糕分成
求最少需要切多少刀。
数据范围:
分析
如果一块蛋糕或若干块蛋糕的总和为
整个总大小是
因此,需要最大化划分组数
问题转化为:将
实现
Step 1
由于
尝试贪心地从小到大枚举(即集合 st 的所有真子集在遍历到 st 之前已经被枚举过)。
这样就可以保证:对于所有的集合,如果选择它作为一个“块”(即该集合内元素和整除
综上,只要当前枚举到的集合可以“单独成块”,就贪心地让它立即“单独成块”。
进而可以保证得到的划分组数是最大的。
Step 2
现在考虑一个“块”的操作次数(切几刀):
已知“块”中元素的下标,可以求得“块”的和 ans 累加
Step 3
每一个块的和可以预处理出来:
枚举到集合 i 时,i 的真子集的和已经求出。
考虑子集
subi 中蛋糕大小的和加上 lowbit 处的蛋糕大小,即可得到集合 i 中蛋糕大小的和。
for (int i = 1; i < (1 << n); i++) {
int lbt = i & (-i), j = __builtin_ctz(lbt);
sum[i] = sum[i ^ lbt] + a[j + 1]; // 1-based
}
总时间复杂度
代码
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const int N = 21;
int n, a[N], m, M, u, ans;
ll s, sum[(1 << N)], T;
int main() {
cin >> n >> m;
M = (1 << n);
for (int i = 1; i <= n; i++) {
cin >> a[i];
s += a[i];
}
T = s / m;
for (int i = 1; i < M; i++) { // 预处理 sum[st]
int lbt = i & (-i), j = __builtin_ctz(lbt);
sum[i] = sum[i ^ lbt] + a[j + 1];
}
for (int st = 1; st < M; st++) { // 优先枚举子集。
if (u & st) { // 如果当前集合 st 中已存在元素被使用过:continue;
continue;
}
ll sst = sum[st];
if (sst % T) { // 如果不能整除:continue;
continue;
}
ans += sst / T - 1; // 分成 k 块需要 k-1 刀。
u |= st; // 标记被使用。
}
cout << ans << '\n';
return 0;
}
:::info[关于GenAI的使用] 本文仅使用 DeepSeek 辅助修改错别字、标点及 Markdown 排版,未用于生成算法思路或代码。文章核心内容均由作者独立完成。 :::