《CF525E Anya 和立方体》
题目描述
Anya 喜欢折叠和粘贴。今天,她决定这样做。
Anya 有n立方体排成一行,编号为1自n从左到右,上面写有自然数。她还k带有感叹号的贴纸。我们知道贴纸的数量不会超过立方体的数量。
Anya 可以在立方体上贴一个感叹号,并得到写在立方体上的数字的阶乘。例如,如果一个 cube 读取5,然后在粘贴后显示为5!,它等于120.
您需要帮助 Anya 数一数有多少种方法可以选择一些方块,然后最多粘在一些选定的方块上k感叹号,以便粘贴后写在所选立方体上的数字之和等于S.Anya 最多可以在每个立方体上贴上一个感叹号。你能做到吗?
如果两种方法具有相同的所选多维数据集集和同一组带感叹号的多维数据集,则认为这两种方式相同。
输入格式
输入的第一行包含三个以空格分隔的整数n,k和S(1≤ n≤25,0≤ k≤ n,1≤ S≤1016)— Anya 拥有的立方体数量和贴纸数量,以及她需要获得的总和。
第二行包含n正整数一个我 (1≤一个我≤109)——写在立方体上的数字。输入中的多维数据集按从左到右的顺序描述,从第一个多维数据集开始。
多个多维数据集可以包含相同的数字。
输出格式
输出选择一定数量的立方体的方法数,并在其中一些立方体上贴上感叹号,使数字之和等于给定的数字S.
隐藏翻译
题意翻译
给你 n 个数,n≤25。 初始序列为 一个我,0≤ 一个我≤109。
你有 k 个 !,每个 ! 可以使序列中的一个数变成 一个我!。 (同一个数至多变一次)
例如 5!=120。
求:选出任意个数使他们和的等于 S 的方案数
输入格式
第一行 n,k,S。
第二行 n 个数,分别代表 一个我。
输出
一行一个整数,选出数的合法方案数。
感谢 @s_a_b_e_r 提供的翻译。
输入输出样例
输入 #1复制
<span style="color:#404040"><span style="background-color:#fafafa">2 2 30
4 3
</span></span>
输出 #1复制
<span style="color:#404040"><span style="background-color:#fafafa">1
</span></span>
输入 #2复制
<span style="color:#404040"><span style="background-color:#fafafa">2 2 7
4 3
</span></span>
输出 #2复制
<span style="color:#404040"><span style="background-color:#fafafa">1
</span></span>
输入 #3复制
<span style="color:#404040"><span style="background-color:#fafafa">3 1 1
1 1 1
</span></span>
输出 #3复制
<span style="color:#404040"><span style="background-color:#fafafa">6
</span></span>
说明/提示
在第一个示例中,唯一的方法是选择两个立方体并在每个立方体上贴上一个感叹号。
在第二个示例中,唯一的方法是选择两个多维数据集,但不要在其中任何一个多维数据集上粘贴感叹号。
在第三个示例中,可以通过三种方式选择任何立方体,我们也可以选择粘贴或不在其上粘贴感叹号。因此,方法的总数为 6 种。
代码实现:
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
typedef long long LL;
// 计算阶乘,注意处理溢出情况
LL factorial(LL x) {
LL res = 1;
for (LL i = 2; i <= x; ++i) {
res *= i;
}
return res;
}
// 生成所有可能的组合及其和与使用的感叹号数量
void generate(vector<pair<LL, LL> >& cubes, map<LL, map<LL, LL> >& sums) {
int n = cubes.size();
for (int mask = 0; mask < (1 << n); ++mask) {
LL sum_normal = 0;
LL sum_fact = 0;
int k_used = 0;
for (int i = 0; i < n; ++i) {
if (mask & (1 << i)) {
sum_normal += cubes[i].first;
sum_fact += cubes[i].second;
k_used++;
}
}
sums[sum_normal][k_used]++;
if (sum_fact != sum_normal) {
sums[sum_fact][k_used]++;
}
}
}
int main() {
int n, k;
LL S;
cin >> n >> k >> S;
vector<LL> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
// 预处理每个数的原始值和阶乘值(防止大数溢出)
vector<pair<LL, LL> > cubes(n);
for (int i = 0; i < n; ++i) {
LL fact = (a[i] <= 20) ? factorial(a[i]) : a[i];
cubes[i] = make_pair(a[i], fact);
}
// 分割数组为左右两部分
int mid = n / 2;
vector<pair<LL, LL> > left(cubes.begin(), cubes.begin() + mid);
vector<pair<LL, LL> > right(cubes.begin() + mid, cubes.end());
// 生成左右两部分的所有可能和
map<LL, map<LL, LL> > left_sums, right_sums;
generate(left, left_sums);
generate(right, right_sums);
// 合并结果,计算符合条件的方案数
LL ans = 0;
for (map<LL, map<LL, LL> >::const_iterator it1 = left_sums.begin(); it1 != left_sums.end(); ++it1) {
LL sum_left = it1->first;
const map<LL, LL>& left_k_counts = it1->second;
LL target = S - sum_left;
map<LL, map<LL, LL> >::const_iterator it2 = right_sums.find(target);
if (it2 == right_sums.end()) {
continue;
}
const map<LL, LL>& right_k_counts = it2->second;
// 遍历所有可能的感叹号使用组合
for (map<LL, LL>::const_iterator lk = left_k_counts.begin(); lk != left_k_counts.end(); ++lk) {
for (map<LL, LL>::const_iterator rk = right_k_counts.begin(); rk != right_k_counts.end(); ++rk) {
if (lk->first + rk->first <= k) {
ans += lk->second * rk->second;
}
}
}
}
cout << ans << endl;
return 0;
}