首先处理出来如果没有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;
}