1 条题解
-
0
Translate:
在一个平面上,有 个点,每个点拥有一个坐标 。若存在三个点 ,,,且 , 时,若第四个点 不在已有的点中,则可以加入 这个点,求最多可以加入几个点。
Solution:
首先看到平面等关键词时,我们首先尝试在草稿纸上画图。
以样例#3为例子:

我们可以发现,第一列列上的每一个点,可以与第一行上的每一个点配对,直到组成一个 的矩形。
这是特例?我们再来一个例子:

乍一看:

我们来分析一下:
首先,我们来手动处理一下前两行,不难发现我们完全可以像第一个例子一样造出一个 的矩形,然后我们再手动模拟一下已经填满了的第二行和第三行能否结合(这边建议在草稿纸上画图),不难发现其实第三行也是可以填满的。以此类推,我们会发现,这张图最后就变成了一个 的矩形。那这个时候,也许大家已经能够找出规律了
,其实这道题就是一个填矩形的游戏。但说了那么多,眼尖的同学其实一眼就能发现,题目中给出的四个点的坐标其实是可以构建一个矩形的……
以上为人脑模拟的思路,下面为代码实现过程。
那问题来了,既然是一个画矩形的题目,那么怎么判断哪一行哪一列是哪一个矩形的呢?
那么要请出我们的一个找归属关系的大佬:并查集。
并查集相关知识,不再赘述,但是我们在这里用并查集可以找出每一个矩形点最多的那一行和那一列,从而由此计算出答案。
我们把每一行、每一列都看成一个个体,那么其实我们就可以把这每一行每一列进行合并,然后通过列的个数判断这个矩形有多少行,通过行的个数判断矩形有多少列,同时对所有处于同一行、同一列的点进行合并。(可能有点绕,但事实如此)。
举个例子更容易理解吧:

对于蓝色这个点,它属于第一行,那么我们就可以将第一行的祖先指向第二列,但是又因为红色点已经被遍历过,并且将其的祖先指向了第一列,那么其实我们的第二个点,便将第一个点的祖先,以及第一个点的祖先的祖先,也就是第一列的祖先,改为了第二列,后面同理,这时,整个已有的点所代表的行与列全部指向了第四列,也就是说,在第一二三四列的所有点,他们的祖先是同一人,也就代表他们在同一个集合内,那么对于每一列呢?我们可以把图旋转一下便同理了。
Code:
#include<bits/stdc++.h> #define int long long #define N 100010 using namespace std; int n, fa[500010], h[500010], l[500010]; int ans; inline int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c>'9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = (x << 3) + (x << 1) + (c ^ '0'); c = getchar(); } return x * f; } int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); } signed main() { n = read(); for (int i = 1;i <= N + N;++i)//因为最多有1e5行和1e5列因此要全部初始化 { fa[i] = i; } for (int i = 1;i <= n;++i) { int x = read(), y = read(); int fx = find(x); int fy = find(y + N); if (fx != fy)//并 { fa[fx] = fy; } } for (int i = 1;i <= N;++i) { ++h[find(i)];//一行最多的个数 } for (int i = N + 1;i <= N + N;++i) { ++l[find(i)]; } for (int i = 1;i <= N + N;++i) { ans += h[i] * l[i]; } printf("%lld", ans - n);//减去一开始有的个数便是答案 return 0; }
- 1
信息
- ID
- 11686
- 时间
- 2000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者