传送门:arc70d
题解
我好菜啊。。。一道
600pts
600
p
t
s
的题想了这么久。。。
首先观察出最后的答案必然为值最小的前几个数(光这个都思考了很久QWQ),简单证明一下:
先将
a
a
排序。
一个数是
unnecessary
u
n
n
e
c
e
s
s
a
r
y
当且仅当所有含有这个数的“好”集合的值
≥k+ai
≥
k
+
a
i
。这样不好想,我们换一种说法:
ai
a
i
是
unnecessary
u
n
n
e
c
e
s
s
a
r
y
当且仅当所有不含有这个数的“坏”集合的值
<k−ai
<
k
−
a
i
<script type="math/tex" id="MathJax-Element-8">< k-a_i</script>。
现在假设
ax(x>1)
a
x
(
x
>
1
)
是
unnecessary
u
n
n
e
c
e
s
s
a
r
y
的,则不包含
ax
a
x
的“坏”集合最大值
<k−ax
<
k
−
a
x
<script type="math/tex" id="MathJax-Element-12">< k-a_x</script>,那么可以证明,
ai(1≤i<x)
a
i
(
1
≤
i
<
x
)
均在这个值最大的“坏”集合当中,采用反证法:若存在一个数
ai(1≤i<x)
a
i
(
1
≤
i
<
x
)
不在这个值最大的“坏”集合中,而这个值
<k−ax
<
k
−
a
x
<script type="math/tex" id="MathJax-Element-15">
k−ax+ai<k
k
−
a
x
+
a
i
<
k
,这个不在“坏”集合中的数完全可以加入集合中使集合的值更大,与“值最大的‘坏’集合”这一定义矛盾。所以
ai(1≤i<x)
a
i
(
1
≤
i
<
x
)
都在这个“坏”集合中。
设这个“坏”集合的值为
val
v
a
l
,由
val<k−ax
v
a
l
<
k
−
a
x
,
val
v
a
l
中含有
ai(1≤i<x)
a
i
(
1
≤
i
<
x
)
得
val+ax−ai<k−ax+ax−ai
v
a
l
+
a
x
−
a
i
<
k
−
a
x
+
a
x
−
a
i
,设
val′=val+ax−ai
v
a
l
′
=
v
a
l
+
a
x
−
a
i
,则
val′<k−ai
v
a
l
′
<
k
−
a
i
。因为
val
v
a
l
是不含
ax
a
x
的最大“坏集合”,我们删去
ai
a
i
加上
ax
a
x
的过程保证了将
val′
v
a
l
′
最大化,所以证明了若
ax
a
x
是
unnecessary
u
n
n
e
c
e
s
s
a
r
y
的,
ai(1≤i<x)
a
i
(
1
≤
i
<
x
)
也一定是
unnecessary
u
n
n
e
c
e
s
s
a
r
y
的。
所以只需要找出最大的
unnecessary
u
n
n
e
c
e
s
s
a
r
y
的
ax
a
x
,答案就是
x
x
。
设表示不包含
ax
a
x
的值最大的“坏”集合的值,
sumx
s
u
m
x
表示
ax
a
x
的前缀和,
f(x,v)
f
(
x
,
v
)
表示
ax−n
a
x
−
n
这个后缀中所有数的集合所能表示出的小于
v
v
的最大值。
由上面的证明得到:,依次枚举判断
valx
v
a
l
x
是否
<k−ax
<
k
−
a
x
<script type="math/tex" id="MathJax-Element-46">< k-a_x</script>即可。
f(x,v)
f
(
x
,
v
)
可用树状数组维护。
代码
#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);
}