题目链接:点击打开链接
解题思路:解法很多,这里说一下怎么用meet-in-middle来做,跟分治很像,就是预先把一半的数据里面的所有结果和全都枚举取来,枚举出来后排个序,然后枚举另一半去二分找相差值,这样就可以用2^(n/2)*log(2^(n/2))找出所有的解。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mx = 1e6 + 10;
int n,m,top,mid,L,R,t1,t2;
ll S,a[mx/100],jie[30],ans;
struct node
{
ll val;
int cnt;
node(){}
node(ll v,int c):val(v),cnt(c){}
bool operator < (node A)const
{
if(val==A.val) return cnt < A.cnt;
return val < A.val;
}
}sum[mx];
void MeetMid(int x,int c,ll v)
{
for(int i=x+1;i<=mid;i++)
{
if(v+a[i]>S) continue;
sum[top].val = v + a[i];
sum[top++].cnt = c;
MeetMid(i,c,sum[top-1].val);
if(a[i]<19&&v+jie[a[i]]<=S&&c<t1){
sum[top].val = v + jie[a[i]];
sum[top++].cnt = c+1;
MeetMid(i,c+1,sum[top-1].val);
}
}
}
void finds(int x,int c,ll v)
{
for(int i=x+1;i<=n;i++)
{
if(v+a[i]>S) continue;
if(v+a[i]==S) ans++;
L = lower_bound(sum,sum+top,node(S-v-a[i],0)) - sum;
R = upper_bound(sum,sum+top,node(S-v-a[i],m-c)) - sum;
ans += R - L;
finds(i,c,v+a[i]);
if(a[i]<19&&v+jie[a[i]]<=S&&c<t2){
L = lower_bound(sum,sum+top,node(S-v-jie[a[i]],0)) - sum;
R = upper_bound(sum,sum+top,node(S-v-jie[a[i]],m-c-1)) - sum;
ans += R - L;
if(v+jie[a[i]]==S) ans++;
finds(i,c+1,v+jie[a[i]]);
}
}
}
int main()
{
jie[0] = 1;
for(int i=1;i<=19;i++) jie[i] = jie[i-1]*i;
scanf("%d%d%I64d",&n,&m,&S);
for(int i=1;i<=n;i++) scanf("%I64d",a+i);
mid = n/2;
t1 = min(m,mid),t2 = min(n-mid,m);
MeetMid(0,0,0);
sort(sum,sum+top);
finds(mid,0,0);
L = lower_bound(sum,sum+top,node(S,0)) - sum;
R = upper_bound(sum,sum+top,node(S,m)) - sum;
printf("%I64d\n",ans+R-L);
return 0;
}