传送门:arc70d
题解
我好菜啊。。。一道
的题想了这么久。。。
首先观察出最后的答案必然为值最小的前几个数(光这个都思考了很久QWQ),简单证明一下:
先将
排序。
一个数
是
当且仅当所有含有这个数的“好”集合的值
。这样不好想,我们换一种说法:
是
当且仅当所有不含有这个数的“坏”集合的值
。
现在假设
是
的,则不包含
的“坏”集合最大值
,那么可以证明,
均在这个值最大的“坏”集合当中,采用反证法:若存在一个数
不在这个值最大的“坏”集合中,而这个值
,所以
,这个不在“坏”集合中的数完全可以加入集合中使集合的值更大,与“值最大的‘坏’集合”这一定义矛盾。所以
都在这个“坏”集合中。
设这个“坏”集合的值为
,由
,
中含有
得
,设
,则
。因为
是不含
的最大“坏集合”,我们删去
加上
的过程保证了将
最大化,所以证明了若
是
的,
也一定是
的。
所以只需要找出最大的
的
,答案就是
。
设
表示不包含
的值最大的“坏”集合的值,
表示
的前缀和,
表示
这个后缀中所有数的集合所能表示出的小于
的最大值。
由上面的证明得到:
,依次枚举判断
是否
即可。
可用树状数组维护。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=5050;
int n,k,a[N],f[N],mx,sum,tre[N];
inline void ad(int x,int val)
{for(;x<=k;x+=(x&(-x))) tre[x]=max(tre[x],val);}
inline int get(int x)
{
int re=0;
for(;x;x-=(x&(-x))) re=max(re,tre[x]);
return re;
}
int main(){
int i,j,res;
scanf("%d%d",&n,&k);
for(i=1;i<=n;++i) {scanf("%d",&a[i]);a[i]=min(a[i],k);sum+=a[i];}
sort(a+1,a+n+1);
for(f[0]=1,mx=0,i=n;i>=1;--i){
sum-=a[i];if(sum<k) res=get(k-sum-1);
mx=min(mx+a[i],k);
for(j=mx;j>=a[i];--j) if(!f[j] && f[j-a[i]]){
ad(j,j);f[j]=1;
}
if(sum>=k) continue;
if(sum+res<k-a[i]) break;
}
printf("%d\n",i);
}