题目:
给你n个数,n≤26 初始序列为ai,1≤ai≤1e9
你有k个! ,每个!可以使序列中的一个数变成ai!(k不一定要用完)
例如5!=120
求:选出任意个数使他们和的等于S的方案数(0≤S≤1e16)
思路:
三种情况深度遍历,贴变阶乘,不贴取原值,不贴不取,把n分一半左右分别遍历;
dfs1当used贴纸数大于k返回;当左区间越过右区间返回;
dfs2当used贴纸数大于k返回;当当前总和大于所有总和返回;当左区间越过右区间,取所有可能使总和-已有和的可能,返回。
20的阶乘大于1e16,所以在阶乘判断时a[i]小于20。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 #include<stack> 7 #include <bitset> 8 #include<set> 9 #include<map> 10 #include<unordered_map> 11 #include<vector> 12 #include<cmath> 13 #include<string> 14 using namespace std; 15 const int N = 30; 16 unordered_map<long long, long long>M[N]; 17 long long fact[N]; 18 int n, k, a[N]; 19 long long s, ans; 20 void dfs1(int now, int end, long long S, int used) { 21 // cout << "dfs1(" << now << "," << end << "," << S << "," << used << ")" << endl; 22 if (used > k) 23 return; 24 if (now > end) { 25 M[used][S] ++; 26 // cout << "M[" << used << "][" << S << "]" << "=" << M[used][S] << endl; 27 return; 28 } 29 dfs1(now + 1, end, S, used); 30 dfs1(now + 1, end, S + a[now], used); 31 if (a[now] <= 20) 32 dfs1(now + 1, end, S + fact[a[now]], used + 1); 33 } 34 35 void dfs2(int now, int end, long long S, int used) { 36 // cout << "dfs1(" << now << "," << end << "," << S << "," << used << ")" << endl; 37 if (used > k) 38 return; 39 if (S > s) 40 return; 41 if (now > end) { 42 for (int i = 0; i <= k - used; i++) { 43 ans += M[i][s - S]; 44 // cout << "ans+=" << M[i][s - S] << " " << "ans=" << ans << endl; 45 } 46 return; 47 } 48 dfs2(now + 1, end, S, used); 49 dfs2(now + 1, end, S + a[now], used); 50 if (a[now] <= 20) 51 dfs2(now + 1, end, S + fact[a[now]], used + 1); 52 } 53 int main() { 54 cin >> n >> k >> s; 55 fact[1] = 1; 56 for (int i = 2; i <= 20; i++) 57 fact[i] = fact[i - 1] * i; 58 for (int i = 1; i <= n; i++) 59 cin >> a[i]; 60 dfs1(1, (n + 1) / 2, 0, 0); 61 cout << endl << endl; 62 dfs2((n + 1) / 2 + 1, n, 0, 0); 63 cout << ans << endl; 64 return 0; 65 }
链接:https://www.luogu.com.cn/problem/solution/CF525E