Square Pasture G
题目链接:jzoj P7153
题目大意
平面上有一些点,问你存在多少种点集,使得有一个四边平行于 xy 轴的正方形可以恰好包住这些点。
思路
由于是一个正方形,我们考虑枚举 x 轴的边或 y 轴的边,然后再减去重复计算的。
(枚举就是选两个点,以它们的 x/y 轴差确定正方形的边长)
首先想怎么要去重,就是它们的 x 轴差等于 y 轴差,那这个对于中途每个搞出来的矩阵计算一下就好了。
那考虑怎么搞矩阵,那首先有几个东西。
这个矩阵一定要包含你一开始选的两个点。
如果那两个点另外一维的距离比这一维的答案就不能搞。
然后不难想到可以拿双指针去搞。
可以枚举右边界,然后算出左边界的上下界,然后就不断统计。
然后具体的统计方式可以看代码,主要就是右边界移动,然后看形成的矩阵是否可能,然后把左边界的不断弹掉,弹到符合下一个的下界,然后弹的过程中那些矩阵都要统计。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct node {
int x, y;
}a[201], tmp[201];
int n, ans, tn, two;
bool cmp(node x, node y) {
if (x.x != y.x) return x.x < y.x;
return x.y < y.y;
}
int abss(int x) {
return (x < 0) ? -x : x;
}
bool cmp1(node x, node y) {
if (x.y != y.y) return x.y < y.y;
return x.x < y.x;
}
void work() {
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++) {//枚举上下边界
int sz = a[j].x - a[i].x;
if (sz < abss(a[j].y - a[i].y)) continue;
tn = 0;
for (int k = i; k <= j; k++) {
tmp[++tn] = a[k];
}
sort(tmp + 1, tmp + tn + 1, cmp1);
int l = 1, r = 0;
while (l <= tn && tmp[l].y < max(a[i].y, a[j].y) - sz) l++;//一开始移动到合法的位置
while (r + 1 <= tn && max(a[i].y, a[j].y) > tmp[r + 1].y) r++;
for (; r <= tn && tmp[r].y <= min(a[i].y, a[j].y) + sz; r++) {
if (r == 0) continue;
int lmin = max(max(a[i].y, a[j].y) - sz, tmp[r].y - sz);//搞出左边界可以的范围
int lmax = min(min(a[i].y, a[j].y), (r + 1 <= tn ? tmp[r + 1].y - sz - 1 : min(a[i].y, a[j].y)));
while (l <= tn && tmp[l].y < lmin) l++;
if (tmp[r].y >= max(a[i].y, a[j].y) || tmp[r + 1].y != max(a[i].y, a[j].y)) ans++;
//右边界移动,除了恰好移动不到位置,别的情况都要加贡献
if (tmp[r].y - tmp[l].y == sz) two++;//去重,如果刚好框柱就是正方形就会计算两遍
while (l + 1 <= tn && tmp[l].y < lmax) {//左边界移动,新的合法情况出现
ans++; l++;
if (tmp[r].y - tmp[l].y == sz) two++;
}
}
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d %d", &a[i].x, &a[i].y);
}
ans = n + 1;
sort(a + 1, a + n + 1, cmp);
work();
for (int i = 1; i <= n; i++)//xy轴翻转再来一次
swap(a[i].x, a[i].y);
sort(a + 1, a + n + 1, cmp);
work();
printf("%d", ans - two / 2);//记得去重,因为计算了两次,所以去重的要除二
return 0;
}