3441: 乌鸦喝水
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 311 Solved: 117
[ Submit][ Status][ Discuss]
Description
Input
Output
Sample Input
Sample Output
对的没错这是道模拟题,一看题目就很容易看出来
题目很好理解,先计算出每个水缸口下降多少次后就再也喝不到了
s[x].val表示第x个水缸口下降s[x].val后就再也喝不到
因为每喝一次所有水缸口都会下降,所以s[].val的大小关系永远不变,并且每次都是当前s[x].val最小的最先喝不了
假设可以喝无数趟,这题就是水题,答案就是max(a[x].val)(1<=x<=n),很好理解因为你每次无论喝哪杯结果都一样,所以你喝的顺序无所谓,最后一定喝了max(a[x].val)(1<=x<=n)次
但是这题你最多跑m趟,这样就有关系了,因为你一趟喝多少水取决于你之前的情况
比如(s[x].val的)数据 1 2 3 4 5,你喝一趟显然能喝5次,但数据5 1 2 3 4你第一趟就只能喝1次了
所以这题每次喝都的前后状态有关,那么怎么写呢,看上面红字
这样就有个大概的思路:每次都看当前s[x].val最小的在第几趟时喝到第几个水缸口时喝完
直到喝完m趟或者所有的水缸口都喝不到结束
思路还算简单处理起来可麻烦
①先算出是在第几趟的时候喝完当前s[x].val最小的水缸口:
这个整除下就好了,假设当前已经有p-1个水缸口喝不到水了,那么趟数就是(s[rak[p]].val-lost)/(n-(p-1)),其中rak[p]表示第p小的水缸口编号,lost表示已经整体下降了lost次(也就是乌鸦已经喝了lost次水)
②之后看喝到第几个编号的水缸时刚好喝完当前s[x].val最小的水缸
这里得用二分+线段树or树状数组判断,找到一个位置r满足r前面刚好有s[x].val个水缸口没有被喝完
这样喝到位置r时,当前s[x].val最小的水缸口刚好被喝完
②到这里可以判断下一个水缸口了
不过因为你这一趟还没有喝完,所以要先把这一趟喝完,注意r = min(r, x)!因为你不知道下一个水缸口在什么地方
有可能在x到r的范围内这样就有可能已经喝不到了,之后分情况讨论。。如果在min(r, x)前面那么肯定不影响,判断这一趟有没有喝完就好了,如果在后面需要继续二分,具体看代码吧
复杂度(nlognlogn)
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define LL long long
typedef struct
{
LL id;
LL val;
}Res;
Res s[100005];
LL n, rak[100005], tre[100005];
bool comp1(Res a, Res b)
{
if(a.val<b.val || a.val==b.val && a.id<b.id)
return 1;
return 0;
}
bool comp2(Res a, Res b)
{
if(a.id<b.id)
return 1;
return 0;
}
void Update(LL x)
{
while(x<=n)
{
tre[x]++;
x += x&-x;
}
}
LL Query(LL x)
{
LL sum = 0;
while(x)
{
sum += tre[x];
x -= x&-x;
}
return sum;
}
int main(void)
{
LL lost;
LL m, p, x, i, now, temp, l, r, mid;
while(scanf("%lld%lld%lld", &n, &m, &p)!=EOF)
{
for(i=1;i<=n;i++)
scanf("%lld", &s[i].val);
for(i=1;i<=n;i++)
{
scanf("%lld", &x);
s[i].val = max((p-s[i].val)/x+1, 0ll);
s[i].id = i;
}
sort(s+1, s+n+1, comp1);
for(i=1;i<=n;i++)
rak[i] = s[i].id;
sort(s+1, s+n+1, comp2);
p = 1, lost = 0;
memset(tre, 0, sizeof(tre));
while(m)
{
while(s[rak[p]].val-lost<=0 && p<=n)
Update(rak[p++]);
if(p>n)
break;
temp = min(m, (s[rak[p]].val-lost)/(n-p+1));
lost += temp*(n-p+1);
m -= temp;
if(m==0)
break;
now = 1;
while(now<=n)
{
while(s[rak[p]].val-lost<=0 && p<=n)
Update(rak[p++]);
temp = n-(now-1)-(Query(n)-Query(now-1));
while(rak[p]<now && s[rak[p]].val-lost-temp<=0 && p<=n)
Update(rak[p++]);
if(p>n)
break;
if(s[rak[p]].val-lost-temp>0)
{
lost += temp;
break;
}
temp = min(s[rak[p]].val-lost, rak[p]-(now-1)-(Query(rak[p])-Query(now-1)));
lost += temp;
l = 0, r = n-now+1;
while(l<r)
{
mid = (l+r+1)/2;
if(now-1+mid-(now-1)-(Query(now-1+mid)-Query(now-1))<=temp)
l = mid;
else
r = mid-1;
}
now = now+r;
Update(rak[p++]);
}
m--;
if(p>n || m==0)
break;
}
printf("%lld\n", lost);
}
return 0;
}