题目链接
https://code.google.com/codejam/contest/8284486/dashboard
题意
给一个 m⋅n 的格点图,求这个格点图中正方形的数目。
思路
包括正正方形和斜正方形。
正正方形数目
我们用 f(i) 表示边长为 i 的正方形数目。
明显可知
设如下格点图的长为m,宽为n(设
m≥n
):
首先我们选定左上角的边长为2的正方形。
- 我们水平向右平移,每次平移一个单位,可以平移 m−2 次,会产生 m−1 个 2⋅2 的正方形。
- 我们将其向下平移,每次平移一个单位,可以平移 n−2 次,会产生 n−1 个 2⋅2 的正方形。
于是我们可知 f(2)=(m−1)(n−1)
同理可以推得 f(3)=(m−2)(n−2) , f(4)=(m−3)(n−3) , f(n)=(m−(n−1))(n−(n−1))
所以我们正正方形的数目:
ans1=f(1)+f(2)+...+f(n)
=∑i=1n(m−(i−1))(n−(i−1))
=∑i=0n−1(m−i)(n−i)
= n(n+1)(3m−n+1)6
斜正方形数目
首先,假设我们已经有一个 i⋅i 的正正方形了,那么正正方形中的最大斜正方形(表示其不能再增大)有多少个呢?我们设 g(i) 为边长为i的正正方形数目包含的最大斜正方形数目。
假设
i=2
:
g(i)=1
假设
i=3
:
g(i)=2
其实观察可知:在边长为 i⋅i 的正正方形里面,最大斜正方形的数目 g(i)=i−1 (选定一条边,除去最两边的2个点,其他点都有最大斜正方形)。
那么,我们每个边长为
i⋅i
的正正方形里面都有
g(i)
个斜正方形,我们边长为
i
的正正方形有
ans2=f(1)g(1)+f(2)g(2)+...+f(n)g(n)
=∑i=1nf(i)g(i)
=∑i=0n−1f(i)g(i)
=∑i=0n−1(m−i)(n−i)(i−1)
=n(2m−n)(n+1)(n−1)12
所以我们最后的结果为: n(n+1)(3m−n+1)6+n(2m−n)(n+1)(n−1)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;
}