9.16-CSP晚间测试-乌鸦喝水(By 5_Lei)

首先处理出来如果没有m轮的限制,每个水缸最多能被喝的次数,记为cnt[i]

根据重要性质:cnt小的一定会在cnt大的之前喝完

所以将cnt升序排序一下

这样我们就可以按cnt从小到大,从1到n挨个处理水缸,每一次处理都一直处理到该水缸喝完,且不用考虑其他水缸,

因为在他之前的肯定被喝完了,在他之后的肯定被喝不完(emm,不太严谨,可能有cnt相等的情况)

然后开始核心部分:

las:在 当前轮次中 原序列中(未按cnt排序的序列)目前处理到的水缸

我们先看能走几整轮,看一下从las到n一共有多少存活的水缸,即为num,如果当前的ans+num <= cnt[i]

那么这一轮可以走,轮数++,las=1(新的一轮从1开始),ans+=num

如果走不了那么需要找到用剩下的次数能走到哪里,在树状数组上二分查找,更新一下las,ans= cnt[i](或者ans+=剩下的cnt[i])




!!! :

树状数组是用来干什么的?

用来维护当前还有多少存活的,注意树状数组 以 未按cnt排序之前的序列 为下标,即如果c[i] = 1,代表原序列中的i还活着,而不是排序后的i

注意二分后的las加一,这可能和写法有关,我的写法是二分的las是能走到的,所以我们要加1,代表这一轮从las+1开始



代码

#include <bits/stdc++.h>

using namespace std;

const int maxn = 2e5+50;

typedef long long ll;

ll R()
{
    ll x = 0,f = 1;
    char c = getchar();
    while(c>'9' || c<'0')
    {
        if(c=='-')f=-1;
        c = getchar();
    }
    while(c>='0' && c<='9')
    {
        x = (x<<1)+(x<<3)+(c^48);
        c = getchar();
    }
    return x*f;
}

struct node
{
    ll cnt,pos;
    bool friend operator < (node a,node b){return a.cnt < b.cnt;}
}g[maxn];

ll a[maxn],w[maxn],c[maxn];

ll lowbit(ll x){return x&(-x);}

void add(ll x,ll d,ll n)
{
    while(x <= n)
    {
        c[x] += d;
        x += lowbit(x);
    }
}

ll query(ll x)
{
    ll ans = 0;
    while(x)
    {
        if(!x)break;
        ans += c[x];
        x -= lowbit(x);
    }
    return ans;
}

ll find(ll pos,ll x,ll n)
{
    ll l = pos,r = n,ans = pos,sum = query(pos-1);
    while(l <= r)
    {
        ll mid = (l+r)>>1;
        if(query(mid)-sum <= x)ans = mid,l = mid+1;
        else r = mid-1;
    }
    return ans;
}

void Lei()
{
    ll n = R(),m = R(),s = R(),ans = 0,tot = 0,las = 1;
    for(int i = 1 ; i <= n ; i++)w[i] = R();
    for(int i = 1 ; i <= n ; i++)
    {
        a[i] = R();
        g[i].cnt = max(0ll,(s-w[i])/a[i]+1);
        g[i].pos = i;
        add(i,1,n);
    }
    sort(g+1,g+n+1);
    for(int i = 1 ; i <= n ; i++)
    {
        if(ans > g[i].cnt || !g[i].cnt){add(g[i].pos,-1,n);continue;}
        while(tot < m && ans < g[i].cnt)
        {
           //我们先看能走几整轮
            ll sum = query(n)-query(las-1);
            if(sum + ans <= g[i].cnt)
            {
                tot++,las = 1,ans += sum;
                if(tot >= m)break;
            }
            else 
            {
                //走不了整轮的话,看能走到哪里
                las = find(las,g[i].cnt-ans,n)+1;//记得加一
                ans = g[i].cnt;
                break;
            }
        }
        add(g[i].pos,-1,n);
    }
    printf("%lld\n",ans);
}

int main()
{
    Lei();
    return 0;
}
posted @ 2022-09-17 20:21  zasdcn  阅读(48)  评论(0编辑  收藏  举报