【JZOJ1404】菱形内的计数【并查集】

题目:

题目链接:https://jzoj.net/senior/#main/show/1404
给出一个菱形,问这个菱形中有多少个内部不含边的平行四边形。


思路:

我们先把这个菱形转换为正方形。例如样例的转换方式如下:
在这里插入图片描述
那么接下来我们就是要求有多少个中间没有边的矩形。
我们可以记录每一个矩形的大小和最右上方(x1,y1)(x1,y1)和最左下方的点(x2,y2)(x2,y2),这样就可以算出每一个矩形的面积。最后枚举每一个矩形,如果满足(x1x2)(y1y2)=s(x1-x2)(y1-y2)=s,那么这就是一个矩形。
如何判断矩形中间有没有边?我们只要枚举每一条边,如果这一条边的两边在一个连通块内,那么就将这一个连通块标记一下即可。


代码:

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

const int N=800010,M=900;
int n,x,y,p1,p2,ans,father[N],MAXx[N],MINx[N],MAXy[N],MINy[N],size[N];
char c,ch[M*2][M*2];
//   \92   /47

int com(int x,int y)
{
	return (x-1)*n+y;
}

int find(int x)
{
	return x==father[x]?x:father[x]=find(father[x]);
}

void merge(int x,int y)
{
	x=find(x),y=find(y);
	if (x==y) return;
	MAXx[x]=max(MAXx[x],MAXx[y]);
	MAXy[x]=max(MAXy[x],MAXy[y]);
	MINx[x]=min(MINx[x],MINx[y]);
	MINy[x]=min(MINy[x],MINy[y]);
	size[x]+=size[y];
	father[y]=x;
}

int main()
{
	scanf("%d\n",&n);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
		{
			int p=com(i,j);
			father[p]=p; size[p]=1;
			MAXx[p]=i+1; MINx[p]=i;
			MAXy[p]=j+1; MINy[p]=j;
		}
	for (int i=1;i<=n*2;i++)
	{
		int j=0;
		while (c=getchar())
		{
			if (c=='\n') break;
			ch[i][++j]=c;
		}
	}
	for (int i=1;i<=n;i++)
		for (int j=2;j<=n;j++)
		{
			x=i+j-1; y=n+1-i+j-1;
			if (ch[x][y]==' ') merge(com(i,j),com(i,j-1));
		}
	for (int i=1;i<=n;i++)
		for (int j=2;j<=n;j++)
		{
			x=i+j-1; y=n+2*i-x;
			if (ch[x][y]==' ') merge(com(j,i),com(j-1,i));
		}
	for (int i=1;i<=n;i++)
		for (int j=2;j<=n;j++)
		{
			x=i+j-1; y=n+1-i+j-1;
			if (ch[x][y]!=' ' && find(com(i,j))==find(com(i,j-1)))
				size[find(com(i,j))]=-233;
		}
	for (int i=1;i<=n;i++)
		for (int j=2;j<=n;j++)
		{
			x=i+j-1; y=n+2*i-x;
			if (ch[x][y]!=' ' && find(com(j,i))==find(com(j-1,i)))
				size[find(com(j,i))]=-233;
		}
	for (int i=1;i<=n*n;i++)
		if (father[i]==i && size[i]==(MAXx[i]-MINx[i])*(MAXy[i]-MINy[i]))
			ans++;
	printf("%d",ans);
	return 0;
}
posted @ 2019-11-09 16:07  全OI最菜  阅读(159)  评论(0编辑  收藏  举报