题解:搜索+二分
对于每个数有选与不选两种情况。然后我们先搜前一半的状态,每个数选还是不选。
有2^17种,然后我将每种状态拍一个序先存着。然后我再搜后一半的状态,2^18种。
假设后一半某一种情况的子集和为w,我们二分的答案为t,那么我们在前一半二分找t-w。
就是前一半和后一半拼成了一个子集。如果子集全在后一半怎么办?因为前一半有个空集,
空集和后一半拼子集还是只在后一半的。
代码:没有评测的地方 我(*゜ロ゜)ノ瞎写的
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 200000 #define LL long long using namespace std; int n,an,k,bn,p,q,a[maxn]; LL l,r,mid,ans,g[maxn],f[maxn]; bool check(LL t){ int all=0; for(int i=1;i<=q;i++){ LL w=t-f[i]; int c=lower_bound(g+1,g+p+1,w)-g; all+=p-c+1; } return all>=k; } int main(){ scanf("%d%d",&n,&k); for(int i=1;i<=n;i++)scanf("%d",&a[i]),r+=a[i]; an=n/2;bn=n-an; for(int st=0;st<(1<<an);st++){ LL all=0; for(int i=0;i<an;i++) if((st>>i)&1)all+=a[i+1]; g[++p]=all; } for(int st=0;st<(1<<bn);st++){ LL all=0; for(int i=0;i<bn;i++) if((st>>i)&1)all+=a[n-i]; f[++q]=all; } sort(g+1,g+p+1);sort(f+1,f+q+1); while(l<=r){ mid=(l+r)>>1; if(check(mid)){ ans=mid;l=mid+1; }else r=mid-1; } printf("%lld\n",ans); return 0; }