CF1146D 青蛙跳

1 CF1146D 青蛙跳

2 题目描述

时间限制 \(2s\) | 空间限制 \(256M\)

一只青蛙开始的位置在数据线的 \(0\) 位置,青蛙有两个魔法正整数 \(a\)\(b\)。从位置 \(k\) 起跳,青蛙可以跳到位置 \(k+a\) 或者 \(k-b\)。在区间 \([0,x]\) 内设 \(f(x)\) 是青蛙可以到达的不同整数。青蛙不需要在一次旅行中访问所有的整数点,也就是如果青蛙从 \(0\) 开始可以到达一个整数,那么这个整数算有效。对于给定的整数 \(m\),找出 \(i\)\(0\)\(m\) 的所有 \(f(i)\) 的累加和。

数据范围:\(1≤𝑚≤10^9,1≤𝑎,𝑏≤10^5\)

3 题解

我们发现:每一个我们到达的数一定是 \(\gcd(a, b)\) 的倍数,这是由于裴蜀定理:\(ax + by = k\)\(k\) 一定为 \(\gcd(a, b)\) 的倍数。但是我们直接统计 \(gcd(a, b)\) 的可能个数又会错,这是因为我们到达当前位置的路上可能会超过 \(i\) 这个限制,而这种情况是不被允许的。但是如果 \(i > a + b - 1\),我们就可以确保所有的 \(gcd(a, b)\) 的倍数的位置可以被拼凑出来。

我们可以将要减的 \(-y\)\(b\) 先统一减去,也就是说每次可以往左跳 \(b\) 就跳。在跳完 \(-y\) 次后,我们就可以通过不停增加 \(a\) 来到达目标了。这里我们最差情况下是在 \(b-1\) 的位置往右跳 \(a\),此时我们到达的位置就是 \(a+b-1\)。如果我们可以满足这种极端情况,那么我们一定可以构造出所有 \(gcd(a, b)\) 的倍数。也就是说,如果 \(i > a+b-1\)\(f(i) = \lfloor \dfrac{i}{\gcd(a, b)} \rfloor + 1\)。此时只需要用等差数列维护即可。对于 \(\le a+b-1\)\(i\),我们可以用 \(dijkstra\) 或者 \(SPFA\) 暴力计算。

4 代码(空格警告):·

#include <iostream>
#include <queue>
#include <cmath>
#include <cstring>
using namespace std;
const int N = 2e5+10;
typedef long long ll;
#define int long long
int m, a, b, ans, fl, cel;
priority_queue< pair<int, int> > q;
int h[N];
int gcd(int x, int y)
{
    if (y == 0) return x;
    return gcd(y, x%y);
}
signed main()
{
    cin >> m >> a >> b;
    memset(h, 0x3f, sizeof(h));
    h[0] = 0;
    q.push(make_pair(0, 0));
    while (q.size())
    {
        pair<int, int> cur;
        cur = q.top();
        q.pop();
        if (-cur.first > h[cur.second]) continue;
        if (cur.second > b && h[cur.second - b] > h[cur.second])
        {
            h[cur.second - b] = h[cur.second];
            q.push(make_pair(-h[cur.second - b], cur.second - b));
        }
        if (cur.second <= b-1 && h[cur.second + a] > max(h[cur.second], cur.second + a))
        {
            h[cur.second + a] = max(h[cur.second], cur.second + a);
            q.push(make_pair(-h[cur.second + a], cur.second + a));
        }
    }
    for (int i = 0; i <= min(m, a+b-1); i++) ans += max(0ll, m - h[i] + 1);
    if (m >= a+b)
    {
        fl = ceil((a+b) / gcd(a, b));
        cel = m / gcd(a, b);
        if (fl <= cel)
        {
            ans += (m + 1) * (cel - fl + 1) - gcd(a, b) * ((fl + cel) * (cel - fl + 1) / 2);
        }
    }
    cout << ans;
    return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-03-08 21:50  David24  阅读(132)  评论(0编辑  收藏  举报