题目:http://codeforces.com/contest/525/problem/E
题意:输入N,K,S。N代表接下来要输入的数字序列{a1,a2...an}的个数,K代表有K次机会marks使其中一个数ai变成ai的阶乘,求选x个数(1<=x<=N)使用y次marks(0<=y<=K)使它们的和为S的方案数。
分析:枚举一半的数字,将结果存进hash表,然后枚举后面一半的数字在hash表里面查询。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <string>
using namespace std;
typedef long long LL;
#define MOD 1000019
#define MAXN 3000000
LL a[10000];
LL fac[]={1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600,6227020800,87178291200,
1307674368000,20922789888000,355687428096000,6402373705728000};
struct node
{
LL value;
LL use;
LL count;
int next;
};
struct Hash
{
LL Table[MOD],cnt;
node List[MAXN];
void Clear()
{
cnt=0ll;
for(int i=0;i<MOD;i++)
Table[i]=-1ll;
}
void Insert(LL v,LL use)
{
LL hash=v%MOD;
LL temp=Table[hash];
while(temp!=-1)
{
if(List[temp].value==v && List[temp].use==use)
{
List[temp].count++;
return ;
}
temp=List[temp].next;
}
List[cnt].value=v;
List[cnt].use=use;
List[cnt].count=1;
List[cnt].next=Table[hash];
Table[hash]=cnt;
cnt++;
}
LL Search(LL v,LL use)
{
LL hash=v%MOD;
hash=Table[hash];
LL ret=0;
while(hash!=-1)
{
if(List[hash].value==v && List[hash].use<=use)
ret+=List[hash].count;
hash=List[hash].next;
}
return ret;
}
}H;
LL S,K,N,ans;
void dfs1(LL cur_n,LL n,LL v,LL u)
{
if(cur_n>n+1 || u>K || v>S)
return ;
if(cur_n==n+1)
{
H.Insert(v,u);
return ;
}
if(a[cur_n]<19)
dfs1(cur_n+1,n,v+fac[a[cur_n]],u+1);
dfs1(cur_n+1,n,v+a[cur_n],u);
dfs1(cur_n+1,n,v,u);
}
void dfs2(LL cur_n,LL n,LL v,LL u)
{
if(cur_n>n+1 || u>K || v>S)
return ;
if(cur_n==n+1)
{
ans+=H.Search(S-v,K-u);
return ;
}
if(a[cur_n]<19)
dfs2(cur_n+1,n,v+fac[a[cur_n]],u+1);
dfs2(cur_n+1,n,v+a[cur_n],u);
dfs2(cur_n+1,n,v,u);
}
int main()
{
LL i,j;
cin>>N>>K>>S;
for(i=0;i<N;i++)
cin>>a[i];
ans=0;
H.Clear();
dfs1(0ll,N/2-1ll,0ll,0ll);
dfs2(N/2ll,N-1ll,0ll,0ll);
cout<<ans<<"\n";
return 0;
}