P5815 [CQOI2010]扑克牌 题解

这篇博客介绍了如何运用二分搜索算法解决CQOI竞赛中的一道题目。博主详细阐述了正确性证明,指出由于问题的单调性,已找到的解一定是下界。在二分搜索过程中,博主给出了check函数的分析,解释了如何判断当前解是否可行,并特别注意了在二分模板中的边界条件,以避免死循环。最后,博主提供了ACCode,即通过测试的代码实现。

首先,我是从一本通那儿过来的,但因为数据范围不同,所以正解代码就不同,那里 n 极其大,但这里确是 c_i 极其大,所以我们必须二分答案。代码不难,但是细节比较多。作为一名 CQ OIer,感触较深。

正确性证明

首先,我们因为要求最优解,所以说我们可以来证明一下,如果已经求出的解为 $ans$,那么答案一定大于等于它,因为这个解已经成立了,不可能取比它小的了,所以满足了单调性。

二分方法

首先,左端点为 0,表示一个都不能成套,最大区间为 $7.5\times 10^8$。最坏数据如下:

2 500000000
500000000 500000000 500000000


此时答案为 7.5\times 10^8,如果不会算,设成 10^9 也无妨。

二分的 check 函数可以这样来分析:

首先,设当前判断的是 x,那么我们就可以这样想:如果它的牌本来就够,那么就用它自己的,把 joker 给其他牌,这样一定能满足,如果给了它,冗余的也没用,所以我们加上的就是 c_i-x,但是我们不能让它变为负数,所以说还要与就求不够的牌数,但是它肯定是自然数,所以还要和 0 取最大值,这就是它缺少的。一共进行 x 轮,每次最多补一个,在保证足够的情况下,一共只能补 k=\min{m,x} 次,所以记录 t 为缺少牌的数量,然后如果 t\le k,那么可以取 x 次,返回 1,否则返回 0。

有了详细的二分,我们只需要套个二分模板,但要注意的是,区间终点必须偏右,否则只有 40 分,原因如下:

首先,但判断到最后一次时,l=r-1,此时 mid=\frac{l+r}{2}=l,如果成立,那么 l=mid 一直执行下去,即一直满足 l<r,等死恒成立,二分不会退出,就会变成死循环,所以说我们在最后一次时区间终点必须偏右。

AC Code:

#include<bits/stdc++.h>
#define int long long //t有可能爆 int
using namespace std;
int n,a[55],m,l,r=7.5e8;//l 左端点,r 右端点
bool check(int x){
    int t=0;
    for(int i=1;i<=n;i++){
        t+=max(x-a[i],0ll);//累加缺少的牌数
        if(t>min(x,m))
            return 0;//如果不够就不行
    }
    return 1;//判断到最后都可以就是可行的
}
signed main(){
    scanf("%lld %lld",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    while(l<r){
        int mid=l+r>>1;
        if(l==r-1)
            mid++;//向上取整,否则40分
        if(check(mid))
            l=mid;
        else
            r=mid-1;//左边可以取,右边不能取
    }//二分模板
    printf("%lld\n",r);//取当前的右端点
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值