题目链接:
[POJ 2002]Squares[二分][数学]
题意分析:
平面中有n个点,求这些点能组成多少个正方形?
解题思路:
最多1e3个点,四个点枚举肯定超时,必须控制在两个点以内。那么两个点可行吗?下面这幅图可以说明可行:
当一条边确定以后,由等边三角形的特性,旁边的两个点也能枚举出来。
所以有(i是a点,j是b点):
temp.x = p[i].x + (p[i].y - p[j].y);
temp.y = p[i].y + (p[j].x - p[i].x);
和
temp.x = p[j].x + (p[i].y - p[j].y);
temp.y = p[j].y + (p[j].x - p[i].x);
然后用二分搜索查找点是否在图中即可。
由于经过坐标排序后,每个矩形会被枚举两次,最终答案要除以2。
例如上图,当枚举边cd和边bd时会枚举到上图的矩形。
个人感受:
第一想法是枚举两条边= =。发现也是复杂度其高啊。一条边出其它点,这种做法真是对我等数学渣的摧残啊。
具体代码如下:
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<string>
#define lowbit(x) (x & (-x))
#define root 1, n, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 1
#define ll long long
#define pii pair<int, int>
#define pr(x) cout << #x << " = " << (x) << '\n';
using namespace std;
const int INF = 0x7f7f7f7f;
const int MAXN = 1e3 + 111;
struct P {
int x, y;
bool operator < (const P &t)const {
if (x == t.x) return y < t.y;
return x < t.x;
}
}p[MAXN], temp;
bool binary(int l, int r) {
if (l > r) return 0;
int m = (l + r) / 2;
if (p[m].x == temp.x && p[m].y == temp.y) return 1;
else if (temp < p[m]) return binary(l, m - 1);
else return binary(m + 1, r);
}
int main()
{
#ifdef LOCAL
freopen("C:\\Users\\apple\\Desktop\\in.txt", "r", stdin);
#endif
int n;
while (~scanf("%d", &n) && n) {
for (int i = 0; i < n; ++i) scanf("%d%d", &p[i].x, &p[i].y);
sort(p, p + n);
int ans = 0;
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
temp.x = p[i].x + (p[i].y - p[j].y);
temp.y = p[i].y + (p[j].x - p[i].x);
//cout << 1 << ':' << temp.x << ' ' << temp.y << '\n';
if (!binary(0, n - 1)) continue;
temp.x = p[j].x + (p[i].y - p[j].y);
temp.y = p[j].y + (p[j].x - p[i].x);
//cout << 2 << ':' << temp.x << ' ' << temp.y << '\n';
if (!binary(0, n - 1)) continue;
++ans;
}
}
printf("%d\n", ans/2);
}
return 0;
}