(color{#0066ff}{题目描述})
今年的世界冰球锦标赛在捷克举行。Bobek 已经抵达布拉格,他不是任何团队的粉丝,也没有时间观念。他只是单纯的想去看几场比赛。如果他有足够的钱,他会去看所有的比赛。不幸的是,他的财产十分有限,他决定把所有财产都用来买门票。
给出 Bobek 的预算和每场比赛的票价,试求:如果总票价不超过预算,他有多少种观赛方案。如果存在以其中一种方案观看某场比赛而另一种方案不观看,则认为这两种方案不同。
(color{#0066ff}{输入格式})
第一行,两个正整数 N 和 M((1 leq N leq 40,1 leq M leq 10^{18})),表示比赛的个数和 Bobek 那家徒四壁的财产。
第二行,N 个以空格分隔的正整数,均不超过 (10^{16}) ,代表每场比赛门票的价格。
(color{#0066ff}{输出格式})
输出一行,表示方案的个数。由于 N 十分大,注意:(答案 le 2^{40})
(color{#0066ff}{输入样例})
5 1000
100 1500 500 500 1000
(color{#0066ff}{输出样例})
8
(color{#0066ff}{题解})
meet in the middle 对抗搜索
(O(2^{40} o 2^{20}+2^{20} o 2^{21}))
合并中间量用upperbound
注意,不能用lowerbound,因为可能有相同价钱的方案,都要算上
#include<cstdio>
#include<queue>
#include<vector>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<cmath>
#define _ 0
#define LL long long
#define Space putchar(' ')
#define Enter putchar('
')
#define fuu(x,y,z) for(int x=(y),x##end=z;x<=x##end;x++)
#define fu(x,y,z) for(int x=(y),x##end=z;x<x##end;x++)
#define fdd(x,y,z) for(int x=(y),x##end=z;x>=x##end;x--)
#define fd(x,y,z) for(int x=(y),x##end=z;x>x##end;x--)
#define mem(x,y) memset(x,y,sizeof(x))
#ifndef olinr
inline char getc()
{
static char buf[100001],*p1=buf,*p2=buf;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100001,stdin),p1==p2)? EOF:*p1++;
}
#else
#define getc() getchar()
#endif
template<typename T>inline void in(T &x)
{
int f=1; char ch; x=0;
while(!isdigit(ch=getc()))(ch=='-')&&(f=-f);
while(isdigit(ch)) x=x*10+(ch^48),ch=getc();
x*=f;
}
LL n,m;
LL a[1<<21],b[1<<21],s[50];
int num1,num2,mid;
LL ans;
inline void dfs1(int dep,LL tot)
{
if(tot>m) return;
if(dep==mid+1)
{
a[++num1]=tot;
return;
}
dfs1(dep+1,tot);
dfs1(dep+1,tot+s[dep]);
}
inline void dfs2(int dep,LL tot)
{
if(tot>m) return;
if(dep==n+1)
{
b[++num2]=tot;
return;
}
dfs2(dep+1,tot);
dfs2(dep+1,tot+s[dep]);
}
int main()
{
in(n),in(m);
fuu(i,1,n) in(s[i]);
mid=(1+n)>>1;
dfs1(1,0LL);
dfs2(mid+1,0LL);
std::sort(a+1,a+num1+1);
fuu(i,1,num2) ans+=std::upper_bound(a+1,a+num1+1,m-b[i])-a-1;
printf("%lld",ans);
return ~~(0^_^0);
}