【AtCoder】ARC70D No Need -DP&贪心

版权声明:侵删,转载请附带链接或评论 https://blog.csdn.net/corsica6/article/details/81742640

传送门:arc70d


题解

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

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/81742640