Codeforces Round #476 (Div. 2)——D. Single-use Stones(二分做法)

Codeforces Round #476 (Div. 2)——D. Single-use Stones(二分做法)

Problem - 965D - Codeforces

A lot of frogs want to cross a river. A river is ww units width, but frogs can only jump l units long, where l<w. Frogs can also jump on lengths shorter than l. but can’t jump longer. Hopefully, there are some stones in the river to help them.

The stones are located at integer distances from the banks. There are aiai stones at the distance of i units from the bank the frogs are currently at. Each stone can only be used once by one frog, after that it drowns in the water.

What is the maximum number of frogs that can cross the river, given that then can only jump on the stones?

Input

The first line contains two integers ww and l (1≤l<w≤10^5) — the width of the river and the maximum length of a frog’s jump.

The second line contains w−1 integers a1,a2,…,aw−1 (0≤ai≤10^4), where aiai is the number of stones at the distance ii from the bank the frogs are currently at.

Output

Print a single integer — the maximum number of frogs that can cross the river.

Examples
input
10 5
0 0 1 0 2 0 0 1 0
output
3

问题解析

看了别的题解都是什么最大流最小割,咱比较笨不理解,转而学习了一下二分的写法。

很容易想到如果mid个青蛙能过河,mid-1肯定也可以,mid+1不一定可以,满足二分性。

至于check函数:

  1. 用双端队列模拟,双端队列存储的是所有青蛙的位置(first)以及这个位置的青蛙个数(second),因为初始青蛙都在位置0,而我们枚举的青蛙数是mid,所以初始队列插入一个{0,mid}。
  2. 遍历数组,当a[i]不为0时,说明有石头,我们从队首取出青蛙放在这个石头上,直到石头放不下青蛙了,或者之前的青蛙全部被放在这个石头上,然后我们再把这个石头的位置和所放青蛙数存入队尾。
  3. 要注意,如果当前位置与队首位置相差距离大于l了,说明当前队首的青蛙是到不了尾部的,因为现在已经到了他们跳不到的地方,而他们还没被放在别的石头,那么他们就只能留在那个石头上那也去不了,既然有一部分去不了队尾,说明我们当前枚举的青蛙数mid是不可行的,返回false。
  4. 最后,判断一下队首的青蛙下标到末尾w的距离是否小于等于l(即队首的青蛙是否能一下子跳到岸上),如果不行,同理3,返回false。如果可以,注意,我们队列首到队列尾存的青蛙位置是逐步接近w的,如果队首的都能跳到岸上,剩下的也行,返回true。

AC代码

const int N = 1e6 + 50;
 
int a[N], n, l;
bool check(int mid)
{
    deque<PII>que;
    que.push_back({ 0,mid });
    for (int i = 1; i < n; i++)
    {
        if (a[i] == 0)continue;
        if (i - que.front().first > l)return 0;
        int x = 0;
        while (!que.empty() && x < a[i])
        {
            auto ans = que.front();
            que.pop_front();
            if (x + ans.second <= a[i])x += ans.second;
            else
            {
                ans.second -= (a[i] - x);
                x = a[i];
                que.push_front(ans);
            }
        }
        que.push_back({ i,x });
    }
    return n - que.front().first <= l;
}
 
void solve()
{
    cin >> n >> l;
    for (int i = 1; i < n; i++)cin >> a[i];
    int l = 0, r = 1e9;
    while (l < r)
    {
        int mid = (l + r + 1) / 2;
        if (check(mid))l = mid;
        else r = mid - 1;
    }
    cout << l;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值