hihocoder 1584 Bounce

31 篇文章 1 订阅

Problem

hihocoder.com/problemset/problem/1584
vjudge.net/problem/HihoCoder-1584

Meaning

一个球在 n * m 的方格图里弹来弹去,开始在左上角,初始方向是向右下角,一直弹到四个角落之一才停下。问球在弹的过程中,经过多少个只经过一次的点
bounce

Analysis

dalao 的解法简直是玄学,我尝试说明一下…
总的思路是用总的路径长度减去两倍那些经过两次的点数(就是图中那些交叉所在的点,点最多被经过两次)。
把路径拆成一段段的,如下图圈出两段:
bounce_segment
首先来计算段长:
设段长为 x,打竖一共有 r 条,打横有 c 条,那么:
(n - 1) / (x - 1) = r
(m - 1) / (x - 1) = c
以题目的图为例,算 r。先忽略第一行(红色圈那行),然后把长度为 3 的段首尾相接地排下来(两段的一个端点共存于一个格),看作是长度为 2 的段首尾不相接地排(蓝色圈出的段),于是段数就是(n - 1) / (x - 1)。打横类似。
calcullate_segment
因为 r 和 c 都要是整数,于是x - 1 = gcd(n - 1, m - 1)(不知道为什么是最大的公约数…)
总的段数就是r * c,而总路程是r * c * (x - 1) + 1。同样的,因为段是首尾相接地连在一起,而连接的地方会算重一个格,所以把段都看成是长为x - 1,但由于最开始的那段(最左上角)没有算重,要补回那个端点。
然后算交叉的点数。观察图形,交叉所在行的最左边,是一个“凹”或者一个“凸”,所在列的最上方也是:
Ou_Tu
行的凹凸数是r - 1,列的凹凸数是c - 1,然后(r - 1) * (c - 1)刚好就是交叉的点的数目的两倍(玄学…)。减两次是因为这些交叉点在算总路程的时候被算了两次(被经过两次),所以要减两次。
cross
“凹凸行”和“凹凸列”相交的那些点,有些是有交叉,而另外一些是没经过的点,为什么是对半分?我们对部分交叉作一点偏移,移成一列全是交叉、一列全是未经过这样的布局(或是按行来移),而刚好交叉列和未经过列是对半分(列凹凸数是偶数,好像行凹凸数和列凹凸数至少有一个是偶数,但不知道为什么),所以,相乘就是交叉点数的两倍。

Code

#include <iostream>
using namespace std;

long long gcd(long long a, long long b)
{
    return b == 0 ? a : gcd(b, a % b);
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    long long n, m;
    while(cin >> n >> m)
    {
        long long len = gcd(n - 1, m - 1) + 1, // 段长
                    r = (n - 1) / (len  - 1),
                    c = (m - 1) / (len - 1);
        long long ans = r * c * (len - 1) + 1; // 总路程
        ans -= (r - 1) * (c - 1); // 减去交叉
        cout << ans << '\n';
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值