将序列分成两半,分别进行暴力,先枚举前一半的所有情况并存下来,然后再枚举后一半的所有情况,并查找符合要求的前一半的种类数。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<iostream>
#define ll long long
using namespace std;
int a[35];
map<ll,int>m[30];
int len;
int n,k;
ll s,f[25];
void init()
{
int i;
f[0]=1;
for(i=1;i<19;i++){
f[i]=f[i-1]*i;
//cout<<f[i]<<endl;
}
}
ll sum;
void dfs(int cur,int used)
{
int i;
if(sum>s) return ;
if(used>k) return ;
if(cur==len){
m[used][sum]++;
return ;
}
dfs(cur+1,used);
sum+=a[cur+1];
dfs(cur+1,used);
sum-=a[cur+1];
if(a[cur+1]<19){
sum+=f[a[cur+1]];
dfs(cur+1,used+1);
sum-=f[a[cur+1]];
}
}
ll ans=0;
ll pre[30];
void dfs2(int cur,int used)
{
int i;
if(sum>s) return ;
if(used>k) return ;
if(cur==n){
for(i=0;i<=k-used;i++){
//if(m[i].find(s-sum)!=m[i].end())
if(m[i].count(s-sum))
ans+=m[i][s-sum];
}
return ;
}
dfs2(cur+1,used);
sum+=a[cur+1];
dfs2(cur+1,used);
sum-=a[cur+1];
if(a[cur+1]<19){
sum+=f[a[cur+1]];
dfs2(cur+1,used+1);
sum-=f[a[cur+1]];
}
}
int main()
{
init();
scanf("%d%d%I64d",&n,&k,&s);
int i;
for(i=1;i<=n;i++){
scanf("%d",a+i);
}
len=n/2;
dfs(0,0);
//for(i=1;i<=k;i++) pre[i]=pre[i-1]+m[100*];
dfs2(len,0);
//cout<<m[2401]<<endl;
cout<<ans<<endl;
return 0;
}