Kickstart 2017 Round A - A.Square Counting(Math)

题目链接

https://code.google.com/codejam/contest/8284486/dashboard

题意

给一个 mn 的格点图,求这个格点图中正方形的数目。

思路

包括正正方形和斜正方形。

正正方形数目

我们用 f(i) 表示边长为 i 的正方形数目。

明显可知f(1)=mn,我们推一下 f(2),f(3)...

设如下格点图的长为m,宽为n(设 mn ):
这里写图片描述
首先我们选定左上角的边长为2的正方形。

  1. 我们水平向右平移,每次平移一个单位,可以平移 m2 次,会产生 m1 22 的正方形。
  2. 我们将其向下平移,每次平移一个单位,可以平移 n2 次,会产生 n1 22 的正方形。

于是我们可知 f(2)=(m1)(n1)

同理可以推得 f(3)=(m2)(n2) , f(4)=(m3)(n3) , f(n)=(m(n1))(n(n1))

所以我们正正方形的数目:

ans1=f(1)+f(2)+...+f(n)

=i=1n(m(i1))(n(i1))

=i=0n1(mi)(ni)

​ = n(n+1)(3mn+1)6

斜正方形数目

首先,假设我们已经有一个 ii 的正正方形了,那么正正方形中的最大斜正方形(表示其不能再增大)有多少个呢?我们设 g(i) 为边长为i的正正方形数目包含的最大斜正方形数目。

假设 i=2 g(i)=1
这里写图片描述

假设 i=3 g(i)=2
这里写图片描述

其实观察可知:在边长为 ii 的正正方形里面,最大斜正方形的数目 g(i)=i1 (选定一条边,除去最两边的2个点,其他点都有最大斜正方形)。

那么,我们每个边长为 ii 的正正方形里面都有 g(i) 个斜正方形,我们边长为 i 的正正方形有f(i)个,那么所有的斜正方形数目为:

ans2=f(1)g(1)+f(2)g(2)+...+f(n)g(n)

=i=1nf(i)g(i)

=i=0n1f(i)g(i)

=i=0n1(mi)(ni)(i1)

=n(2mn)(n+1)(n1)12

所以我们最后的结果为: n(n+1)(3mn+1)6+n(2mn)(n+1)(n1)12

代码

#include <bits/stdc++.h>

using namespace std;

inline int in() {int x; scanf("%d", &x); return x;}
#define pr(x) {cout << #x << ' ' << x << endl;}
#define LL long long
const LL mod = 1e9 + 7;

LL power(LL a, LL n) {
    LL res = 1;
    while (n) {
        if (n & 1) res *= a, res %= mod;
        a *= a; a %= mod;
        n >>= 1;
    }
    return res % mod;
}

LL DIV(LL a, LL b) {
    LL t = power(b, mod - 2);
    return a * t % mod;
}

LL mul(LL a, LL b) {
    return a * b % mod;
}

inline LL add(LL a, LL b) {
    return a + b < mod ? a + b : a + b - mod;
}

int main() {
    int T = in(), kase = 1;
    while (T--) {
        LL m, n;
        cin >> m >> n;
        m--, n--;
        if (m < n) swap(m, n);
        LL v1 = mul(n, n + 1);
        LL v2 = (3 * m - n + 1) % mod;
        LL res1 = mul(v1, v2);
        res1 = DIV(res1, 6);
        LL v3 = (2 * m - n) % mod;
        v3 = mul(n, v3);
        LL v4 = mul(n + 1, n - 1);
        LL res2 = mul(v3, v4);
        res2 = DIV(res2, 12);
        LL ans = add(res1, res2);
        cout << "Case #" << kase++ << ": " << ans << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值