CodeForce div2 Round #763

C-Balanced Stone Heaps

  • 题目描述:给定\(n\)堆石子,规定第\(i(3\le i\le n)\)堆石子能够能够从中取出\(3d\)个石子(\(0<3d<h_i\),其中\(h_i\)为第\(i\)堆石子的数量),其中\(d\)个石子放入第\(i-1\)堆,\(2d\)个石子放入第\(i-2\)堆石子之中,我们从第\(3\)个石子往第\(i\)个石子遍历一次,问所有堆中石子数目最少的堆的石子数目最多是多少
  • 求解“最少中最多”和“最多中最少”问题,我们显然可以联想到二分答案,而本题的关键之处便是在于如何对枚举的答案进行检验
  • 正向考虑这个问题,对于第\(i(2\le i\le n-2)\)个位置,我们总是有两个来源,而如何从这两个来源中进行分配,,以拼凑出当前枚举的答案\(x\)相对较难考虑
  • 所以我们逆向考虑这个问题,从第\(n\)个元素向前考虑, 每个元素扣除一定份额向前分配,要匹配枚举出的答案\(x\)相对容易,所以我们在检验答案时逆向考虑
  • 而逆向考虑还有一个注意点,对于当前位置\(i\),我们有原本石子数\(h_i\)和增量\(b_i\),由于我们是自前向后遍历,所以我们只能利用原本石子数\(h_i\)来向前分配,而增量是无法用以向前分配的
  • 所以我们在\(b_i\)小于\(x\)时,我们判断\(h_i+b_i\)是否大于枚举答案\(x\),当大于时,我们从\(h_i\)中扣除\(x-b_i\)部分使得其能满足标准答案\(x\),剩余\(h_i\)部分尽可能进行分配
  • 如果是小于,我们可以直接认为这个答案不可取,因为对于第\(i\)个元素已经不可能满足,直接\(break\)即可
  • 最后因第\(1,2\)个元素无需向前分配,对其作特殊判断即可
  • 本题中还有一个关键要点,增量\(b_i\)如果使用数组进行存储的话,每次检查时都需要\(memset\),这会导致较大的时间复杂度,并在第二个测试样例超时,所以我们使用向量来存储增量,并在每次检查时根据元素数量进行定义
  • 参考代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn =2e5+5;
#define ll long long
// 二分答案
// 如何进行检验?
// 检验方法是本道题的关键

int T,n;
ll a[maxn];

int check(int x){
    int flag=0;
    vector<int> b(n+10);
    // 本题卡memset,
    for(int i=n;i>=3;i--){
        if(b[i]>=x){
            ll t=a[i]/3;
            b[i-1]+=t;
            b[i-2]+=2*t;
        }else if(a[i]+b[i]>=x){
            ll t=(a[i]-(x-b[i]))/3;
            b[i-1]+=t;
            b[i-2]+=2*t;
        }else{
            flag=1;
            break;
        }
    }
    if(a[1]+b[1]<x||a[2]+b[2]<x)flag=1;
    return !flag;

}

int main(){
    cin>>T;
    while(T--){
        cin>>n;
        ll minx=1e9,maxx=0;
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            minx=min(minx,a[i]);
            maxx=max(maxx,a[i]);
        }
        ll l=minx,r=maxx;
        ll ans;
        while(l<=r){
            ll mid=(l+r)>>1;
            if(check(mid)){
                ans=mid;
                l=mid+1;
            }else r=mid-1;
        }
        cout<<ans<<endl;
    }

    return 0;
}
上一篇:半年总结2021-10-31


下一篇:第3周 基本数据类型