题解:AT_abc458_g [ABC458G] Children Yearn for the Evil Kindergarten

题目链接:洛谷 AtCoder


有趣的题。

思路

首先,容易发现答案有单调性,考虑二分答案,设当前二分到了 \(m\),问题就变成了判断能否让 \(m\) 个人逃离。

对其他的 \(10^{100}-m\) 个人,既然他们逃离不了,那让他们在第一天退出就行了,所以只用贪心地考虑 \(m\) 个人。设一个人在第 \(i\) 天退出的总花费是 \(V_j=\sum_{j=1}^{i} B_j+C_j\) ,那对于任意的 \(i\),如果存在 \(j>i,V_j\leq V_i\),那么 \(i\) 肯定是不优的,因为将第 \(i\) 天退出的所有人变成在第 \(j\) 天退出能让花费更少,且第 \(j\) 天的奖牌更多,不会出现奖牌不够的情况。最后能贡献的 \(i\) 对应的 \(V_i\) 一定是单增的,所以可以用单调栈维护 \(V_i\)

然后直接从前往后贪心地考虑就行了……吗?实际上连样例都过不了,因为如果前面退出的人过多,会使剩下的奖牌数过少,不够剩下的人上交 \(B_i\) 个了。考虑反悔,从前面已经退出的人中选 \(V\) 最大的人重新加回来,直到满足条件。这看似需要用堆来维护,但实际上因为 \(V_i\) 已经是单增的了,所以选最近一次退出的人就行了,可以用栈维护,使复杂度少一个 \(\log\)。具体实现时需要把在同一天退出的人一起维护。

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
const int N=3e5+5;

int n,a[N],b[N],c[N],v[N],st1[N],flag[N],top;
pii st2[N];

bool check(int m)
{
    int sum=0,res=0;
    top=0;
    for(int i=1;i<=n&&m;i++)
    {
        res+=a[i]-m*b[i];;
        sum+=b[i];
        while(res<0&&top)
        {
            pii p=st2[top--];
            if(p.first<=sum)
                return 0;
            int k=min(p.second,(-res-1)/(p.first-sum)+1);
            res+=k*(p.first-sum);
            m+=k;
            if(k<p.second)
                st2[++top]={p.first,p.second-k};
        }
        if(res<0)
            return 0;
        if(flag[i])
        {
            int k=min(m,res/c[i]);
            st2[++top]={sum+c[i],k};
            res-=k*c[i];
            m-=k;    
        }
    }
    if(m)
        return 0;
    return 1;
}

void solve()
{
    top=0;
    for(int i=1;i<=n;i++)
        flag[i]=0;
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
        v[i]=v[i-1]-c[i-1]+b[i]+c[i];
        while(top&&v[i]<=v[st1[top]])
            top--;
        st1[++top]=i;
    }
    for(int i=1;i<=top;i++)
        flag[st1[i]]=1;
    int l=0,r=1e9,ans=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid))
        {
            l=mid+1;
            ans=mid;
        }
        else
            r=mid-1;
    }
    printf("%lld\n",ans);
}

signed main()
{
    int T;
    scanf("%lld",&T);
    while(T--)
        solve();
    return 0;
}
posted @ 2026-08-24 18:57  ShanLing3  阅读(3)  评论(0)    收藏  举报
//雪花飘落效果