atcoder 131 F - Must Be Rectangular!

题目链接

F - Must Be Rectangular!

Time Limit: 2 sec / Memory Limit: 1024 MB

Score :
600
points

Problem Statement

There are
N
dots in a two-dimensional plane. The coordinates of the i-th dot are (\(x_i,y_i\)).

We will repeat the following operation as long as possible:

Choose four integers a, b, c, d (a

c
,
b

d
)
such that there are dots at exactly three of the positions
(
a
,
b
)
,
(
a
,
d
)
,
(
c
,
b
)
and
(
c
,
d
)
, and add a dot at the remaining position.

We can prove that we can only do this operation a finite number of times. Find the maximum number of times we can do the operation.

Constraints

  • 1≤N≤\(10^5\)

....

详见题目链接

思路

将每一个点拆成两个点即横纵各一点

然后将该两点连线

好处

自然将横坐标相等或纵坐标相等的若干点构成一个联通块

是由(0,1),(1,1),(1,2),(1,0),(2,2)通过以上方法构成的

统一将纵坐标加上一个10的目的是避免x,y不能相互区分

然后 这样做了有什么用吗?

答案是肯定的

用并查集来维护

for(i = 1;i <= n;i++)
	{
		int u,v;
      //u-x,v-y
		scanf("%d%d",&u,&v);
		f[FindSet(u)] = FindSet(v + MAXN);
	}

再设数组difx[]dify[]

dif即difference用来统计横坐标或纵坐标的不同数的多少

因为单纯点数 可能在行或列上有重合

for(i = 1;i <= MAXN;i++)
	difx[FindSet(i)]++;
for(i = MAXN + 1;i <= MAXN * 2;i++)
	dify[FindSet(i)]++;

1(0,1)

2(1,1)

3(1,0)

4(1,2)

5(2,2)

如图 的话 横坐标或纵坐标的不同数的多少各为3

乘起来为整个矩阵点数 再减去已有的 就是答案

但考虑到 可能不止一个联通块 所以需要一个枚举的操作

for(i = 1;i <= MAXN * 2;i++)
	ans += 1ll * difx[i] * dify[i];

但聪明的同学可能会想到

如果那些构不成(横或纵相等的在3个以下)的被误算了怎么办

for(i = 1;i <= MAXN;i++)
	difx[FindSet(i)]++;
for(i = MAXN + 1;i <= MAXN * 2;i++)
	dify[FindSet(i)]++;

先前的代码 是加在祖先上的

如果只有一个点(该点所在的行和列无它点)第一个FindSet(i) = 该点的纵坐标加MAXN

可见只加了1 和 后面的减n是抵消了的

然后 又会有同学提到 并查集的初始化

	for(i = 1;i <= MAXN * 2;i++) f[i] = i;

公平地给所有赋了值 包括一些实际上不存在的点

那么是否有可能再次造成误算呢?

这位同学 您又错了

因为对于一个不存在的i
difx[FindSet(i)]++;即difx[i]++;

而ans加时加的是difx[FindSet(i)]*dify[FindSet(i)]

细节

difx的i和dify的i是错开枚举的

即difx[i]>0定有dify[i]

反之亦然

所以不会干扰结果

代码

#include <cstdio>
#include <vector>
#include <algorithm>
 
using namespace std;

const int MAXN = 100000,MAX = 200005;
int f[MAX],difx[MAX],dify[MAX];
inline int FindSet(int a)
{
	if(a == f[a]) return a;
	return f[a] = FindSet(f[a]);
}
int main()
{
    int i,n; 
	scanf("%d",&n);
	for(i = 1;i <= MAXN * 2;i++) f[i] = i;
	for(i = 1;i <= n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		f[FindSet(u)] = FindSet(v + MAXN);
       //不能写成f[FindSet(u)] = v + MAXN;
       //否则会RE我也不知道为什么QAQ
	}
	for(i = 1;i <= MAXN;i++)
		difx[FindSet(i)]++;
	for(i = MAXN + 1;i <= MAXN * 2;i++)
		dify[FindSet(i)]++;
	long long ans = 0;
	for(i = 1;i <= MAXN * 2;i++)
		ans += 1ll * difx[i] * dify[i];
	printf("%lld",ans - n);
    return 0;
}
posted @ 2019-08-08 19:17  resftlmuttmotw  阅读(213)  评论(0编辑  收藏  举报