Factorials and Powers of Two—CF1646C
思路
实在没想到这个题居然是暴力。
我们先不考虑选择的阶乘数字和指数幂数字中有重复的情况。因为 1 ≤ n ≤ 1 0 12 1\le n\le 10^{12} 1≤n≤1012,并且 15 ! > 1 0 12 15!~>~10^{12} 15! > 1012,所以我们可以暴力枚举这 14 14 14 个阶乘数是否选择。
对于每一种阶乘数的选择情况,计算 n n n 还有多少需要用指数幂数字“填充”,我们记这个大小作 l e a v e leave leave。
需要用的指数幂数字的个数为 l e a v e leave leave 的二进制中数字 1 1 1 的个数。这里利用二进制非常巧妙,我没有想到QWQ。
现在我们回到刚开始的那个问题:如果最优选择的方案中选择了数字 x x x,并且在阶乘数字和指数幂数字中都选择了 x x x,那么这种最优解就是不符合题意的。
这里需要证明一个性质:最优解中一定不会在阶乘数字和指数幂数字中都选择一个相同的数字。
证明:
我们假设同时选择了数字
x
x
x,那么我们完全可以把这两次选择变成一次选择,即选择一个指数幂数字
2
∗
x
2~*~x
2 ∗ x。如果
2
∗
x
2~*~x
2 ∗ x 之前已经选择了,我们也可以合并这两个
2
∗
x
2~*~x
2 ∗ x 为一个
4
∗
x
4~*~x
4 ∗ x。很容易看出来这个过程不会无限循环下去。
C o d e Code Code
#include <bits/stdc++.h>
#define int long long
#define sz(a) ((int)a.size())
#define all(a) a.begin(), a.end()
using namespace std;
using PII = pair<int, int>;
using i128 = __int128;
const int N = 2e5 + 10;
int n;
int a[20], la;
int res;
int dfs(int start, int leave, int num) {
if (num >= res) { // 最优化剪枝
return num;
}
if (start == la + 1) {
while (leave) {
if (leave & 1) {
num ++;
}
leave >>= 1;
}
res = min(res, num);
return num;
}
res = min(res, dfs(start + 1, leave, num));
if (leave >= a[start]) {
res = min(res, dfs(start + 1, leave - a[start], num + 1));
}
return res;
}
void solve() {
cin >> n;
la = 0;
int base = 1;
for (int i = 1; i * base <= n; i ++) {
base *= i;
a[++ la] = base;
}
res = 1e18;
dfs(1, n, 0); // 暴力枚举选择的阶乘数字都有哪些
cout << " ";
cout << res << "\n";
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
cin >> T; cin.get();
while (T --) solve();
return 0;
}