题面有毒。。poisonous poi!poi!poi!
不要忘了斜着的正方形也是要算的。
这种题考试时还是不要盼着能A了。。
这篇咋这么多阅读量。。
统计棋盘内格点能围成的正方形的数目,其中有些格点不能作为正方形的顶点。
如果没有删除点,总的方案数显然是
接下来考虑每个被删除的点,其对上半,左半,右半,下半部分的影响类似,重复计算的就是正着的正方形的个数,即长宽的较小值。
用 (l,r,h) 表示一个区域,删除的点在底边界上,左边有l个坐标,右边有r个坐标。
考虑(6+6)*6的区域。
倾斜0格的有6个,1格的有5个,2格的有4个,……,5格的有1个,6格的有6个,总的是27个。
如果是(6+6)*5的区域,那么就是5,4,3,2,1,5了。
如果是(2+2)*5的区域,那么就是2,2,2,2。
令 z=min{l+r,h}
我们先假设高度要不大于左右侧,那么此时的答案就是 z(z+3)2 。
如果大于了左右侧,那么考虑减去多计算的部分,如果左右侧补全到z,那么多出来的部分即 n=z−l或z−r ,公式即为 n(n+1)2 。
然后任意枚举2个点,然后就可以统计被删除2个点的、3个、4个点的正方形的个数了,注意斜的和正的都要统计。
注意一下点的顺序问题,就没啥了。
#include
#include
#include
using namespace std;
typedef long long ll;
const int N = 2005;
const ll mod = 1e8 + 7;
ll n, m, k;
struct Point {
int x, y;
Point(int a = 0, int b = 0) : x(a), y(b) { }
bool operator< (const Point &b) const { return x < b.x || x == b.x && y < b.y; }
bool operator== (const Point &b) const { return x == b.x && y == b.y; }
} pt[N];
ll nonRemoved() {
int k = min(n, m), i; ll ans = 0;
for (i = 1; i <= k; ++i) (ans += (n - i + 1) * (m - i + 1) % mod * i) %= mod;
return ans;
}
ll onceRemoved0(int l, int r, int h) {
int z = min(l + r, h);
if (z == 0) return 0;
ll ans = z * (z + 3) / 2;
if (z > l) ans -= (z - l) * (z - l + 1) / 2;
if (z > r) ans -= (z - r) * (z - r + 1) / 2;
return ans;
}
ll onceRemoved(int x, int y) {
int t = x, b = n - x, l = y, r = m - y;
return (onceRemoved0(t, b, l) + onceRemoved0(t, b, r) + onceRemoved0(l, r, t) + onceRemoved0(l, r, b)
- min(l, t) - min(t, r) - min(r, b) - min(b, l)) % mod;
}
bool inMap(int x, int y) {
return 0 <= x && x <= n && 0 <= y && y <= m;
}
void count(Point a, Point b, int &cnt2, int &cnt3, int &cnt4) {
if (inMap(a.x, a.y) && inMap(b.x, b.y)) {
t = mp.count(a) + mp.count(b);
++cnt2;
if (t > 0) ++cnt3;
if (t > 1) ++cnt4, ++cnt3;
}
}
int main() {
set mp;
int i, j, r, t, x, y, dx, dy;
scanf("%lld%lld%lld", &n, &m, &k);
ll ans = nonRemoved();
for (i = 0; i < k; ++i) {
scanf("%d%d", &pt[i].x, &pt[i].y); mp.insert(pt[i]);
(ans -= onceRemoved(pt[i].x, pt[i].y)) %= mod;
}
ll cnt2 = 0, cnt3 = 0, cnt4 = 0;
for (i = 0; i < k; ++i) {
Point p = pt[i];
for (j = i + 1; j < k; ++j) {
Point q = pt[j];
dx = p.x - q.x, dy = p.y - q.y;
count(Point(p.x + dy, p.y - dx), Point(q.x + dy, q.y - dx), cnt2, cnt3, cnt4);
count(Point(p.x - dy, p.y + dx), Point(q.x - dy, q.y + dx), cnt2, cnt3, cnt4);
if ((abs(dx) + abs(dy)) & 1) continue;
x = dx - dy >> 1, y = dx + dy >> 1;
count(Point(p.x - x, p.y - y), Point(q.x + x, q.y + y), cnt2, cnt3, cnt4);
}
}
ans = (ans + cnt2 - cnt3 / 3 + cnt4 / 6) % mod;
if (ans < 0) ans += mod;
printf("%lld\n", ans);
}
上帝说,不要圆,要方,于是便有了这道题。由于我们应该方,而且最好能够尽量方,所以上帝派我们来找正方形
上帝把我们派到了一个有N行M列的方格图上,图上一共有(N+1)×(M+1)个格点,我们需要做的就是找出这些格点形
成了多少个正方形(换句话说,正方形的四个顶点都是格点)。但是这个问题对于我们来说太难了,因为点数太多
了,所以上帝删掉了这(N+1)×(M+1)中的K个点。既然点变少了,问题也就变简单了,那么这个时候这些格点组成
了多少个正方形呢?
第一行三个整数 N, M, K, 代表棋盘的行数、 列数和不能选取的顶点个数。 保证 N, M >= 1, K <=(N + 1) ×
(M + 1)。约定每行的格点从上到下依次用整数 0 到 N 编号,每列的格点依次用 0到 M 编号。接下来 K 行,每
行两个整数 x,y 代表第 x 行第 y 列的格点被删掉了。保证 0 <=x <=N<=10^6, 0 <=y<=M<=10^6,K<=2*1000且不
会出现重复的格点。
仅一行一个正整数, 代表正方形个数对 100000007( 10^8 + 7) 取模之后的值
2 2 4
1 0
1 2
0 1
2 1
1