题意:给出n个棍子, k个魔法棒, 目标数s. 魔法棒可以使得棍子长度变为棍子长度的阶乘, 问木棒和为s的方案数.
预处理求出阶乘, 根据s范围可知木棒最长是18, 而后进行dfs, dfs时, 分为三种情况, 取这根木棒, 不取这根木棒, 以及取这根木棒的阶乘.
进行两次dfs, 折半存储结果.,这次map开了两维,其实这种思想用的很多,比如区间求中位数,以后多多注意下。
详情看代码
#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(int i=a;i<b;i++)
#define rep1(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e6+100;
ll arr[N];
ll n,k,s,ans;
ll fac[20];
map<ll,ll>mp[30];
void dfs(int x,int cnt,ll sum)
{
if(cnt>k||sum>s) return ;
if(x==n/2)
{
mp[cnt][sum]++;
return ;
}
//以下三种情况
dfs(x+1,cnt,sum);
dfs(x+1,cnt,sum+arr[x]);
if(arr[x]<=18)dfs(x+1,cnt+1,sum+fac[arr[x]]);
}
void dfs1(int x,int cnt,ll sum)
{
if(cnt>k||sum>s) return ;
if(x==n)
{
for(int i=0;i+cnt<=k;i++)
{
if(mp[i].count(s-sum))//这里需要注意,不能用if(mp[i][s-sum]),会延长很多时间,导致超时
ans+=mp[i][s-sum];
}
//ans+=mp[i][s-sum];
return ;
}
dfs1(x+1,cnt,sum);
dfs1(x+1,cnt,sum+arr[x]);
if(arr[x]<=18)dfs1(x+1,cnt+1,sum+fac[arr[x]]);
}
int main()
{
ios::sync_with_stdio(false);
fac[0]=1;
rep(i,1,19) fac[i]=fac[i-1]*i;
cin>>n>>k>>s;
rep(i,0,n) cin>>arr[i];
sort(arr,arr+n);
ans=0;
dfs(0,0,0);//取小于n/2个的棍子
dfs1(n/2,0,0);//取大于等于n/2个的棍子
cout<<ans<<endl;
return 0;
}