给出
n
≤
40
n\leq40
n≤40个比赛,给出每个比赛的票价以及
M
≤
1
e
18
M\leq1e18
M≤1e18表示你的积蓄。然后求问能看的比赛的方案数是多少。
n
n
n如果能够再小一些就可以直接暴搜了,但是搜不得。但是拆成两半然后把两边所有的可行方案的总和全部保存下来,对左边的某个方案
x
x
x,到右边二分小于等于
M
−
x
M-x
M−x的个数求和。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=40;
int a[N],mid;
ll c[1<<19],d[1<<19];
int tot1=0,tot2=0;
int n,mod;
void dfs1(int cur,int ed,ll sum) {
if(cur==ed+1) { c[++tot1]=sum; return; }
dfs1(cur+1,ed,sum);
dfs1(cur+1,ed,(sum+a[cur])%mod);
}
void dfs2(int cur,int ed,ll sum) {
if(cur==ed+1) { d[++tot2]=sum; return; }
dfs2(cur+1,ed,sum);
dfs2(cur+1,ed,(sum+a[cur])%mod);
}
int main() {
scanf("%d%d",&n,&mod);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
mid=n/2;
dfs1(1,mid,0);
dfs2(mid+1,n,0);
sort(c+1,c+tot1+1);
sort(d+1,d+tot2+1);
tot1=unique(c+1,c+tot1+1)-(c+1);
tot2=unique(d+1,d+tot2+1)-(d+1);
ll ans=0;
for(int i=1;i<=tot1;i++) {
int id=lower_bound(d+1,d+tot2+1,mod-c[i])-d;
if(id!=1) {
id--;
ans=max(ans,c[i]+d[id]);
}
}
ans=max(ans,(c[tot1]+d[tot2])%mod);
printf("%lld\n",ans);
return 0;
}