AtCoder题解——HHKB Programming Contest 2020——D - Squares

题目相关

题目链接

HHKB Programming Contest 2020 D 题,https://atcoder.jp/contests/hhkb2020/tasks/hhkb2020_d

请允许我开启吐槽模式,这题当时看到的时候,第一反应一定是数学相关的问题。惭愧的是,这个题目整整考虑了两天才想明白。实在是太菜了。

Problem Statement

Given are integers N, A, and B.

We will place a white square whose side is of length N on the coordinate plane so that the vertices are at (0,0), (N,0), (0,N), and (N,N).

Then, we will place a blue square whose side is of length A and a red square whose side is of length B so that these squares are inside the white square (including its boundary).

Here, each side of these squares must be parallel to the x- or y-axis.

Additionally, each vertex of red and blue squares must be at an integer point.

Find the number of ways to place red and blue squares so that they do not strictly overlap (they may share boundary with each other), modulo 1,000,000,007.

Solve this problem for T test cases in each input file.

Input

Input is given from Standard Input in the following format:

T

Then, T test cases follow, each of which is in the format below:

N A B

Output

For each test case, print the number of ways to place red and blue squares so that they do not strictly overlap, modulo 1,000,000,007. Use one line for each test case.

Samples1

Sample Input 1

3
3 1 2
4 2 2
331895368 154715807 13941326

Sample Output 1

20
32
409369707

Explaination

For example, in the first test case, there are 9 ways to place the blue squares and 4 ways to place the red squares, ignoring overlap.

Wherever we place the red square, there are 4 ways to place the blue square so that it strictly overlaps with the red square.

Thus, there are 9×4−4×4=20 ways to place red and blue squares so that they do not strictly overlap.

Note that the squares may share boundary with each other. For example, when the bottom-left vertices of blue and red squares are (0,0) and (0,1), respectively, the squares do not strictly overlap.

Constraints

  • 1 ≤ T ≤ 10^5
  • 1 ≤ N ≤ 10^9
  • 1 ≤ A ≤ N
  • 1 ≤ B ≤ N
  • All values in input are integers.

题解报告

题目翻译

给一个 N*N、a*a 和 b*b 的正方形,要求用 a*a 和 b*b 大小的正方形来覆盖 N*N 正方形。但是 a*a 和 b*b 的正方形不能重叠,边界可以相邻,而且要求正方形的四角坐标必须是整数。问一共有多少种方法。

题目分析方法一

这个题目,看到后,就知道是一个数学题。因为问有多少种方法,而不是写出具体的方法。如果本题改为具体的方法,那就是一个搜索问题。

非常惭愧,想了半天都没有思路,然后去问同门詹师兄。感谢詹师兄给出如下的详细解释。

有没有感受到数学博士的严谨,完全就是一个论文的写法。说真,没完全看懂,还麻烦师兄面对面解释了三十分钟。

由于师兄考虑的比价复杂,而且给出的思路是一个 O(N^2) 的复杂度,对于题目的数据量来说,肯定是超时的。

题目分析方法二

由于本题的数据量比较大,算法复杂度肯定要控制在 O(1) 这个级别,也就是说只需要一个公式进行计算。

本题是一个二维平面问题。如下图所示。

这我们可以将这个二维问题投影到一维进行考虑。那么就变成了线段覆盖问题。下面我们来分类讨论一下。

放置不了

当且仅当 a+b>n 的时候,这样方案数为 0。这是本题最简单的情况。

下面的重点放在讨论可以放置上。

X 轴不重合,Y 轴随意

这样问题就变为如下图所示:

由于线段 A 和 B 的长度是固定的,因此我们只需要考虑左端点即可。这样,A 的左端点坐标区间为 [0, n-a] 区间,B 的左端点坐标区间为 [0, n-b]。

为了进一步简化问题,我们可以假设线段 A 在线段 B 的左边。

A 左端点坐标为 0,那么线段 A 的坐标为 [0, a];B 可以放置的区间为 [a, n-b],这样一共有 n-b-a+1 种方法;

A 左端点坐标为 1,那么线段 A 的坐标为 [1, a+1];B 可以放置的区间为 [a+1, n-b],这样一共有 n-b-a 种方法;

...

A 左端点坐标为 n-a-b,那么线段 A 的坐标为 [n-a-b, n-b];B 可以放置的区间为 [n-b, n],这样一共有 1 种方法;

这样,我们可以获得一个等差序列,1+2+3+...+(n-b-a)+(n-b-a+1)=\frac{(n-b-a+1)((n-b-a+1)+1)}{2}。记 d=n-a-b,公式变为 \frac{(d+1)(d+2)}{2}

注意,我们目前考虑线段 A 在线段 B 的左边,如果线段 B 在线段 A 的左边,同样有 \frac{(d+1)(d+2)}{2} 种方法。

因此,X 轴不重合,两个线段有 (d+1)(d+2) 种方法。

由于 Y 轴是任意放置,有 (n-a+1)(n-b+1),可以参考詹师兄给出的 lemma。

使用乘法原理,这样的方法一共有 (d+1)(d+2)(n-a+1)(n-b+1)

X 轴重合,Y 轴不重合

这样问题就变为如下图所示:

根据上面的讨论,X 轴的总放置方法为:(n-a+1)(n-b+1),不重合的方法为:(d+1)(d+2)。因此 X 轴重合的方案数量为:(n-a+1)(n-b+1)-(d+1)(d+2)

Y 轴不重合,那么方案数为:(d+1)(d+2)

因此,同样利用乘法原理,总放置方法为:[(n-a+1)(n-b+1)-(d+1)(d+2)][(d+1)(d+2)]

这样,综上所述,答案可以表达为:(d+1)(d+2)(n-a+1)(n-b+1)+[(n-a+1)(n-b+1)-(d+1)(d+2)][(d+1)(d+2)],还可以进一步化简:(d+1)(d+2)(n-a+1)(n-b+1)+[(n-a+1)(n-b+1)-(d+1)(d+2)][(d+1)(d+2)]=(d+1)(d+2)[(n-a+1)(n-b+1)+(n-a+1)(n-b+1)-(d+1)(d+2)]=(d+1)(d+2)[2(n-a+1)(n-b+1)-(d+1)(d+2)]

我们完成了完整的数学分析,由于本题数据比较大,需要进行取模运算。因此从最终的推导式,我们可以得出,需要计算的有以下几个:

\left\{\begin{matrix} x=(d+1)(d+2)\\ y=(n-a+1)\\ z=(n-b+1) \end{matrix}\right.

最终的答案为:2xyz-x^{2}

AC 参考代码

//https://atcoder.jp/contests/hhkb2020/tasks/hhkb2020_c
//HHKB Programming Contest 2020
//D - Squares
/*数学题*/
#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
const int MO = 1e9+7;

int main() {
    int t;
    cin>>t;

    while (t--) {
        LL n, a, b;
        cin>>n>>a>>b;

        LL ans = 0;
        LL d=n-a-b;
        if (d>=0) {
            LL x=(d+1)*(d+2)%MO;
            LL y=n-a+1;
            LL z=n-b+1;
            ans=2*x*(y*z%MO)%MO-x*x%MO;
            ans=(ans+MO)%MO;
        }

        cout<<ans<<"\n";
    }

    return 0;
}

再次感叹一下,数学好难。用了两天才想明白,感觉给掏空了,已经凉了。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的老周

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值